1 // Copyright 2017 The Chromium Authors
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/file_enumerator.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/containers/circular_deque.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/logging.h"
16 #include "base/strings/string_util.h"
17 #include "build/build_config.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 #if BUILDFLAG(IS_ANDROID)
22 #include "base/android/content_uri_utils.h"
23 #include "base/test/android/content_uri_test_utils.h"
24 #endif
25
26 using testing::ElementsAre;
27 using testing::IsEmpty;
28 using testing::UnorderedElementsAre;
29
30 namespace base {
31 namespace {
32
33 const FilePath::StringType kEmptyPattern;
34
35 const std::vector<FileEnumerator::FolderSearchPolicy> kFolderSearchPolicies{
36 FileEnumerator::FolderSearchPolicy::MATCH_ONLY,
37 FileEnumerator::FolderSearchPolicy::ALL};
38
39 struct TestFile {
TestFilebase::__anon524d774b0111::TestFile40 TestFile(const FilePath::CharType* file_name, const char* c)
41 : path(file_name), contents(c) {}
42
TestFilebase::__anon524d774b0111::TestFile43 TestFile(const FilePath::CharType* directory,
44 const FilePath::CharType* file_name,
45 const char* c)
46 : path(FilePath(directory).Append(file_name)), contents(c) {}
47
48 const FilePath path;
49 const std::string contents;
50 File::Info info;
51 bool found = false;
52 };
53
54 struct TestDirectory {
TestDirectorybase::__anon524d774b0111::TestDirectory55 explicit TestDirectory(const FilePath::CharType* n) : name(n) {}
56 const FilePath name;
57 File::Info info;
58 bool found = false;
59 };
60
CheckModificationTime(const FileEnumerator::FileInfo & actual,Time expected_last_modified_time)61 void CheckModificationTime(const FileEnumerator::FileInfo& actual,
62 Time expected_last_modified_time) {
63 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
64 // On POSIX, GetLastModifiedTime() rounds down to the second, but
65 // File::GetInfo() does not.
66 Time::Exploded exploded;
67 expected_last_modified_time.UTCExplode(&exploded);
68 exploded.millisecond = 0;
69 EXPECT_TRUE(Time::FromUTCExploded(exploded, &expected_last_modified_time));
70 #endif
71 EXPECT_EQ(actual.GetLastModifiedTime(), expected_last_modified_time);
72 }
73
CheckFileAgainstInfo(const FileEnumerator::FileInfo & actual,TestFile & expected)74 void CheckFileAgainstInfo(const FileEnumerator::FileInfo& actual,
75 TestFile& expected) {
76 EXPECT_FALSE(expected.found)
77 << "Got " << expected.path.BaseName().value() << " twice";
78 expected.found = true;
79 EXPECT_EQ(actual.GetSize(), int64_t(expected.contents.size()));
80 CheckModificationTime(actual, expected.info.last_modified);
81 }
82
CheckDirectoryAgainstInfo(const FileEnumerator::FileInfo & actual,TestDirectory & expected)83 void CheckDirectoryAgainstInfo(const FileEnumerator::FileInfo& actual,
84 TestDirectory& expected) {
85 EXPECT_FALSE(expected.found) << "Got " << expected.name.value() << " twice";
86 expected.found = true;
87 CheckModificationTime(actual, expected.info.last_modified);
88 }
89
RunEnumerator(const FilePath & root_path,bool recursive,int file_type,const FilePath::StringType & pattern,FileEnumerator::FolderSearchPolicy folder_search_policy)90 circular_deque<FilePath> RunEnumerator(
91 const FilePath& root_path,
92 bool recursive,
93 int file_type,
94 const FilePath::StringType& pattern,
95 FileEnumerator::FolderSearchPolicy folder_search_policy) {
96 circular_deque<FilePath> rv;
97 FileEnumerator enumerator(root_path, recursive, file_type, pattern,
98 folder_search_policy,
99 FileEnumerator::ErrorPolicy::IGNORE_ERRORS);
100 for (auto file = enumerator.Next(); !file.empty(); file = enumerator.Next())
101 rv.emplace_back(std::move(file));
102 return rv;
103 }
104
CreateDummyFile(const FilePath & path)105 bool CreateDummyFile(const FilePath& path) {
106 return WriteFile(path, byte_span_from_cstring("42"));
107 }
108
GetFileInfo(const FilePath & file_path,File::Info & info)109 bool GetFileInfo(const FilePath& file_path, File::Info& info) {
110 // FLAG_WIN_BACKUP_SEMANTICS: Needed to open directories on Windows.
111 File f(file_path,
112 File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WIN_BACKUP_SEMANTICS);
113 if (!f.IsValid()) {
114 LOG(ERROR) << "Could not open " << file_path.value() << ": "
115 << File::ErrorToString(f.error_details());
116 return false;
117 }
118 if (!f.GetInfo(&info)) {
119 std::string last_error = File::ErrorToString(File::GetLastFileError());
120 LOG(ERROR) << "Could not get info about " << file_path.value() << ": "
121 << last_error;
122 return false;
123 }
124
125 return true;
126 }
127
SetUpTestFiles(const ScopedTempDir & temp_dir,std::vector<TestFile> & files)128 void SetUpTestFiles(const ScopedTempDir& temp_dir,
129 std::vector<TestFile>& files) {
130 for (TestFile& file : files) {
131 const FilePath file_path = temp_dir.GetPath().Append(file.path);
132 ASSERT_TRUE(WriteFile(file_path, file.contents));
133 ASSERT_TRUE(GetFileInfo(file_path, file.info));
134 }
135 }
136
137 } // namespace
138
TEST(FileEnumerator,NotExistingPath)139 TEST(FileEnumerator, NotExistingPath) {
140 const FilePath path = FilePath::FromUTF8Unsafe("some_not_existing_path");
141 ASSERT_FALSE(PathExists(path));
142
143 for (auto policy : kFolderSearchPolicies) {
144 const auto files = RunEnumerator(
145 path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
146 FILE_PATH_LITERAL(""), policy);
147 EXPECT_THAT(files, IsEmpty());
148 }
149 }
150
TEST(FileEnumerator,EmptyFolder)151 TEST(FileEnumerator, EmptyFolder) {
152 ScopedTempDir temp_dir;
153 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
154
155 for (auto policy : kFolderSearchPolicies) {
156 const auto files =
157 RunEnumerator(temp_dir.GetPath(), true,
158 FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
159 kEmptyPattern, policy);
160 EXPECT_THAT(files, IsEmpty());
161 }
162 }
163
TEST(FileEnumerator,SingleFileInFolderForFileSearch)164 TEST(FileEnumerator, SingleFileInFolderForFileSearch) {
165 ScopedTempDir temp_dir;
166 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
167
168 const FilePath& path = temp_dir.GetPath();
169 const FilePath file = path.AppendASCII("test.txt");
170 ASSERT_TRUE(CreateDummyFile(file));
171
172 for (auto policy : kFolderSearchPolicies) {
173 const auto files = RunEnumerator(
174 temp_dir.GetPath(), true, FileEnumerator::FILES, kEmptyPattern, policy);
175 EXPECT_THAT(files, ElementsAre(file));
176 }
177 }
178
TEST(FileEnumerator,SingleFileInFolderForDirSearch)179 TEST(FileEnumerator, SingleFileInFolderForDirSearch) {
180 ScopedTempDir temp_dir;
181 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
182
183 const FilePath& path = temp_dir.GetPath();
184 ASSERT_TRUE(CreateDummyFile(path.AppendASCII("test.txt")));
185
186 for (auto policy : kFolderSearchPolicies) {
187 const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
188 kEmptyPattern, policy);
189 EXPECT_THAT(files, IsEmpty());
190 }
191 }
192
TEST(FileEnumerator,SingleFileInFolderWithFiltering)193 TEST(FileEnumerator, SingleFileInFolderWithFiltering) {
194 ScopedTempDir temp_dir;
195 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
196
197 const FilePath& path = temp_dir.GetPath();
198 const FilePath file = path.AppendASCII("test.txt");
199 ASSERT_TRUE(CreateDummyFile(file));
200
201 for (auto policy : kFolderSearchPolicies) {
202 auto files = RunEnumerator(path, true, FileEnumerator::FILES,
203 FILE_PATH_LITERAL("*.txt"), policy);
204 EXPECT_THAT(files, ElementsAre(file));
205
206 files = RunEnumerator(path, true, FileEnumerator::FILES,
207 FILE_PATH_LITERAL("*.pdf"), policy);
208 EXPECT_THAT(files, IsEmpty());
209 }
210 }
211
TEST(FileEnumerator,TwoFilesInFolder)212 TEST(FileEnumerator, TwoFilesInFolder) {
213 ScopedTempDir temp_dir;
214 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
215
216 const FilePath& path = temp_dir.GetPath();
217 const FilePath foo_txt = path.AppendASCII("foo.txt");
218 const FilePath bar_txt = path.AppendASCII("bar.txt");
219 ASSERT_TRUE(CreateDummyFile(foo_txt));
220 ASSERT_TRUE(CreateDummyFile(bar_txt));
221
222 for (auto policy : kFolderSearchPolicies) {
223 auto files = RunEnumerator(path, true, FileEnumerator::FILES,
224 FILE_PATH_LITERAL("*.txt"), policy);
225 EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt));
226
227 files = RunEnumerator(path, true, FileEnumerator::FILES,
228 FILE_PATH_LITERAL("foo*"), policy);
229 EXPECT_THAT(files, ElementsAre(foo_txt));
230
231 files = RunEnumerator(path, true, FileEnumerator::FILES,
232 FILE_PATH_LITERAL("*.pdf"), policy);
233 EXPECT_THAT(files, IsEmpty());
234
235 files =
236 RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
237 EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt));
238 }
239 }
240
TEST(FileEnumerator,SingleFolderInFolderForFileSearch)241 TEST(FileEnumerator, SingleFolderInFolderForFileSearch) {
242 ScopedTempDir temp_dir;
243 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
244
245 const FilePath& path = temp_dir.GetPath();
246
247 ScopedTempDir temp_subdir;
248 ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
249
250 for (auto policy : kFolderSearchPolicies) {
251 const auto files =
252 RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
253 EXPECT_THAT(files, IsEmpty());
254 }
255 }
256
TEST(FileEnumerator,SingleFolderInFolderForDirSearch)257 TEST(FileEnumerator, SingleFolderInFolderForDirSearch) {
258 ScopedTempDir temp_dir;
259 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
260
261 const FilePath& path = temp_dir.GetPath();
262
263 ScopedTempDir temp_subdir;
264 ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
265
266 for (auto policy : kFolderSearchPolicies) {
267 const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
268 kEmptyPattern, policy);
269 EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath()));
270 }
271 }
272
TEST(FileEnumerator,TwoFoldersInFolder)273 TEST(FileEnumerator, TwoFoldersInFolder) {
274 ScopedTempDir temp_dir;
275 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
276
277 const FilePath& path = temp_dir.GetPath();
278
279 const FilePath subdir_foo = path.AppendASCII("foo");
280 const FilePath subdir_bar = path.AppendASCII("bar");
281 ASSERT_TRUE(CreateDirectory(subdir_foo));
282 ASSERT_TRUE(CreateDirectory(subdir_bar));
283
284 for (auto policy : kFolderSearchPolicies) {
285 auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
286 kEmptyPattern, policy);
287 EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, subdir_bar));
288
289 files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
290 FILE_PATH_LITERAL("foo"), policy);
291 EXPECT_THAT(files, ElementsAre(subdir_foo));
292 }
293 }
294
TEST(FileEnumerator,FolderAndFileInFolder)295 TEST(FileEnumerator, FolderAndFileInFolder) {
296 ScopedTempDir temp_dir;
297 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
298
299 const FilePath& path = temp_dir.GetPath();
300
301 ScopedTempDir temp_subdir;
302 ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
303 const FilePath file = path.AppendASCII("test.txt");
304 ASSERT_TRUE(CreateDummyFile(file));
305
306 for (auto policy : kFolderSearchPolicies) {
307 auto files =
308 RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
309 EXPECT_THAT(files, ElementsAre(file));
310
311 files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
312 kEmptyPattern, policy);
313 EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath()));
314
315 files = RunEnumerator(path, true,
316 FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
317 kEmptyPattern, policy);
318 EXPECT_THAT(files, UnorderedElementsAre(file, temp_subdir.GetPath()));
319 }
320 }
321
TEST(FileEnumerator,FilesInParentFolderAlwaysFirst)322 TEST(FileEnumerator, FilesInParentFolderAlwaysFirst) {
323 ScopedTempDir temp_dir;
324 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
325
326 const FilePath& path = temp_dir.GetPath();
327
328 ScopedTempDir temp_subdir;
329 ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
330 const FilePath foo_txt = path.AppendASCII("foo.txt");
331 const FilePath bar_txt = temp_subdir.GetPath().AppendASCII("bar.txt");
332 ASSERT_TRUE(CreateDummyFile(foo_txt));
333 ASSERT_TRUE(CreateDummyFile(bar_txt));
334
335 for (auto policy : kFolderSearchPolicies) {
336 const auto files =
337 RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
338 EXPECT_THAT(files, ElementsAre(foo_txt, bar_txt));
339 }
340 }
341
TEST(FileEnumerator,FileInSubfolder)342 TEST(FileEnumerator, FileInSubfolder) {
343 ScopedTempDir temp_dir;
344 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
345
346 const FilePath subdir = temp_dir.GetPath().AppendASCII("subdir");
347 ASSERT_TRUE(CreateDirectory(subdir));
348
349 const FilePath file = subdir.AppendASCII("test.txt");
350 ASSERT_TRUE(CreateDummyFile(file));
351
352 for (auto policy : kFolderSearchPolicies) {
353 auto files = RunEnumerator(temp_dir.GetPath(), true, FileEnumerator::FILES,
354 kEmptyPattern, policy);
355 EXPECT_THAT(files, ElementsAre(file));
356
357 files = RunEnumerator(temp_dir.GetPath(), false, FileEnumerator::FILES,
358 kEmptyPattern, policy);
359 EXPECT_THAT(files, IsEmpty());
360 }
361 }
362
TEST(FileEnumerator,FilesInSubfoldersWithFiltering)363 TEST(FileEnumerator, FilesInSubfoldersWithFiltering) {
364 ScopedTempDir temp_dir;
365 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
366
367 const FilePath test_txt = temp_dir.GetPath().AppendASCII("test.txt");
368 const FilePath subdir_foo = temp_dir.GetPath().AppendASCII("foo_subdir");
369 const FilePath subdir_bar = temp_dir.GetPath().AppendASCII("bar_subdir");
370 const FilePath foo_test = subdir_foo.AppendASCII("test.txt");
371 const FilePath foo_foo = subdir_foo.AppendASCII("foo.txt");
372 const FilePath foo_bar = subdir_foo.AppendASCII("bar.txt");
373 const FilePath bar_test = subdir_bar.AppendASCII("test.txt");
374 const FilePath bar_foo = subdir_bar.AppendASCII("foo.txt");
375 const FilePath bar_bar = subdir_bar.AppendASCII("bar.txt");
376 ASSERT_TRUE(CreateDummyFile(test_txt));
377 ASSERT_TRUE(CreateDirectory(subdir_foo));
378 ASSERT_TRUE(CreateDirectory(subdir_bar));
379 ASSERT_TRUE(CreateDummyFile(foo_test));
380 ASSERT_TRUE(CreateDummyFile(foo_foo));
381 ASSERT_TRUE(CreateDummyFile(foo_bar));
382 ASSERT_TRUE(CreateDummyFile(bar_test));
383 ASSERT_TRUE(CreateDummyFile(bar_foo));
384 ASSERT_TRUE(CreateDummyFile(bar_bar));
385
386 auto files =
387 RunEnumerator(temp_dir.GetPath(), true,
388 FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
389 FILE_PATH_LITERAL("foo*"),
390 FileEnumerator::FolderSearchPolicy::MATCH_ONLY);
391 EXPECT_THAT(files,
392 UnorderedElementsAre(subdir_foo, foo_test, foo_foo, foo_bar));
393
394 files = RunEnumerator(temp_dir.GetPath(), true,
395 FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
396 FILE_PATH_LITERAL("foo*"),
397 FileEnumerator::FolderSearchPolicy::ALL);
398 EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, foo_foo, bar_foo));
399 }
400
TEST(FileEnumerator,InvalidDirectory)401 TEST(FileEnumerator, InvalidDirectory) {
402 ScopedTempDir temp_dir;
403 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
404
405 const FilePath test_file = temp_dir.GetPath().AppendASCII("test_file");
406 ASSERT_TRUE(CreateDummyFile(test_file));
407
408 // Attempt to enumerate entries at a regular file path.
409 FileEnumerator enumerator(test_file, /*recursive=*/true,
410 FileEnumerator::FILES, kEmptyPattern,
411 FileEnumerator::FolderSearchPolicy::ALL,
412 FileEnumerator::ErrorPolicy::STOP_ENUMERATION);
413 FilePath path = enumerator.Next();
414 EXPECT_TRUE(path.empty());
415
416 // Slightly different outcomes between Windows and POSIX.
417 #if BUILDFLAG(IS_WIN)
418 EXPECT_EQ(File::Error::FILE_ERROR_FAILED, enumerator.GetError());
419 #else
420 EXPECT_EQ(File::Error::FILE_ERROR_NOT_A_DIRECTORY, enumerator.GetError());
421 #endif
422 }
423
424 #if BUILDFLAG(IS_POSIX)
TEST(FileEnumerator,SymLinkLoops)425 TEST(FileEnumerator, SymLinkLoops) {
426 ScopedTempDir temp_dir;
427 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
428
429 const FilePath subdir = temp_dir.GetPath().AppendASCII("subdir");
430 ASSERT_TRUE(CreateDirectory(subdir));
431
432 const FilePath file = subdir.AppendASCII("test.txt");
433 ASSERT_TRUE(CreateDummyFile(file));
434
435 const FilePath link = subdir.AppendASCII("link");
436 ASSERT_TRUE(CreateSymbolicLink(temp_dir.GetPath(), link));
437
438 auto files = RunEnumerator(
439 temp_dir.GetPath(), true,
440 FileEnumerator::FILES | FileEnumerator::DIRECTORIES, kEmptyPattern,
441 FileEnumerator::FolderSearchPolicy::MATCH_ONLY);
442
443 EXPECT_THAT(files, UnorderedElementsAre(subdir, link, file));
444
445 files = RunEnumerator(subdir, true,
446 FileEnumerator::FILES | FileEnumerator::DIRECTORIES |
447 FileEnumerator::SHOW_SYM_LINKS,
448 kEmptyPattern,
449 FileEnumerator::FolderSearchPolicy::MATCH_ONLY);
450
451 EXPECT_THAT(files, UnorderedElementsAre(link, file));
452 }
453 #endif
454
455 // Test FileEnumerator::GetInfo() on some files and ensure all the returned
456 // information is correct.
TEST(FileEnumerator,GetInfo)457 TEST(FileEnumerator, GetInfo) {
458 ScopedTempDir temp_dir;
459 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
460
461 std::vector<TestFile> files = {
462 TestFile(FILE_PATH_LITERAL("file1"), "First"),
463 TestFile(FILE_PATH_LITERAL("file2"), "Second"),
464 TestFile(FILE_PATH_LITERAL("file3"), "Third-third-third")};
465 SetUpTestFiles(temp_dir, files);
466
467 #if BUILDFLAG(IS_ANDROID)
468 FilePath root_dir =
469 *base::test::android::GetInMemoryContentTreeUriFromCacheDirDirectory(
470 temp_dir.GetPath());
471 #else
472 FilePath root_dir = temp_dir.GetPath();
473 #endif
474 FileEnumerator file_enumerator(
475 root_dir, false, FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
476 while (!file_enumerator.Next().empty()) {
477 auto info = file_enumerator.GetInfo();
478 bool found = false;
479 for (TestFile& file : files) {
480 if (info.GetName() == file.path.BaseName()) {
481 CheckFileAgainstInfo(info, file);
482 found = true;
483 break;
484 }
485 }
486
487 EXPECT_TRUE(found) << "Got unexpected result " << info.GetName().value();
488 }
489
490 for (const TestFile& file : files) {
491 EXPECT_TRUE(file.found)
492 << "File " << file.path.value() << " was not returned";
493 }
494 }
495
496 // Test that FileEnumerator::GetInfo() works when searching recursively. It also
497 // tests that it returns the correct information about directories.
TEST(FileEnumerator,GetInfoRecursive)498 TEST(FileEnumerator, GetInfoRecursive) {
499 ScopedTempDir temp_dir;
500 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
501
502 TestDirectory directories[] = {TestDirectory(FILE_PATH_LITERAL("dir1")),
503 TestDirectory(FILE_PATH_LITERAL("dir2-empty")),
504 TestDirectory(FILE_PATH_LITERAL("dir3")),
505 TestDirectory(FILE_PATH_LITERAL("dir4"))};
506
507 for (const TestDirectory& dir : directories) {
508 const FilePath dir_path = temp_dir.GetPath().Append(dir.name);
509 ASSERT_TRUE(CreateDirectory(dir_path));
510 }
511
512 std::vector<TestFile> files = {
513 TestFile(FILE_PATH_LITERAL("dir1"), FILE_PATH_LITERAL("file1"), "First"),
514 TestFile(FILE_PATH_LITERAL("dir1"), FILE_PATH_LITERAL("file2"), "Second"),
515 TestFile(FILE_PATH_LITERAL("dir3"), FILE_PATH_LITERAL("fileA"),
516 "Third-third-3"),
517 TestFile(FILE_PATH_LITERAL("dir4"), FILE_PATH_LITERAL(".file"), "Dot")};
518 SetUpTestFiles(temp_dir, files);
519
520 // Get last-modification times for directories. Must be done after we create
521 // all the files.
522 for (TestDirectory& dir : directories) {
523 const FilePath dir_path = temp_dir.GetPath().Append(dir.name);
524 ASSERT_TRUE(GetFileInfo(dir_path, dir.info));
525 }
526
527 #if BUILDFLAG(IS_ANDROID)
528 FilePath root_dir =
529 *base::test::android::GetInMemoryContentTreeUriFromCacheDirDirectory(
530 temp_dir.GetPath());
531 #else
532 FilePath root_dir = temp_dir.GetPath();
533 #endif
534 FileEnumerator file_enumerator(
535 root_dir, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
536 while (!file_enumerator.Next().empty()) {
537 auto info = file_enumerator.GetInfo();
538 bool found = false;
539 if (info.IsDirectory()) {
540 for (TestDirectory& dir : directories) {
541 if (info.GetName() == dir.name) {
542 CheckDirectoryAgainstInfo(info, dir);
543 found = true;
544 break;
545 }
546 }
547 } else {
548 for (TestFile& file : files) {
549 if (info.GetName() == file.path.BaseName()) {
550 CheckFileAgainstInfo(info, file);
551 #if BUILDFLAG(IS_ANDROID)
552 std::string expected =
553 temp_dir.GetPath().BaseName().Append(file.path.DirName()).value();
554 EXPECT_EQ(base::JoinString(info.subdirs(), "/"), expected);
555 #endif
556 found = true;
557 break;
558 }
559 }
560 }
561
562 EXPECT_TRUE(found) << "Got unexpected result " << info.GetName().value();
563 }
564
565 for (const TestDirectory& dir : directories) {
566 EXPECT_TRUE(dir.found) << "Directory " << dir.name.value()
567 << " was not returned";
568 }
569 for (const TestFile& file : files) {
570 EXPECT_TRUE(file.found)
571 << "File " << file.path.value() << " was not returned";
572 }
573 }
574
575 #if BUILDFLAG(IS_FUCHSIA)
576 // FileEnumerator::GetInfo does not work correctly with INCLUDE_DOT_DOT.
577 // https://crbug.com/1106172
578 #elif BUILDFLAG(IS_WIN)
579 // Windows has a bug in their handling of ".."; they always report the file
580 // modification time of the current directory, not the parent directory. This is
581 // a bug in Windows, not us -- you can see it with the "dir" command (notice
582 // that the time of . and .. always match). Skip this test.
583 // https://crbug.com/1119546
584 #else
585 // Tests that FileEnumerator::GetInfo() returns the correct info for the ..
586 // directory.
TEST(FileEnumerator,GetInfoDotDot)587 TEST(FileEnumerator, GetInfoDotDot) {
588 ScopedTempDir temp_dir;
589 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
590
591 const FilePath::CharType kSubdir[] = FILE_PATH_LITERAL("subdir");
592 const FilePath subdir_path = temp_dir.GetPath().Append(kSubdir);
593 ASSERT_TRUE(CreateDirectory(subdir_path));
594
595 std::vector<TestFile> files = {
596 TestFile(kSubdir, FILE_PATH_LITERAL("file1"), "First"),
597 TestFile(kSubdir, FILE_PATH_LITERAL("file2"), "Second"),
598 TestFile(kSubdir, FILE_PATH_LITERAL("file3"), "Third-third-third")};
599 SetUpTestFiles(temp_dir, files);
600
601 TestDirectory dotdot(FILE_PATH_LITERAL(".."));
602 // test_dir/subdir/.. is just test_dir.
603 ASSERT_TRUE(GetFileInfo(temp_dir.GetPath(), dotdot.info));
604
605 FileEnumerator file_enumerator(subdir_path, false,
606 FileEnumerator::FILES |
607 FileEnumerator::DIRECTORIES |
608 FileEnumerator::INCLUDE_DOT_DOT);
609 while (!file_enumerator.Next().empty()) {
610 auto info = file_enumerator.GetInfo();
611 bool found = false;
612 if (info.IsDirectory()) {
613 EXPECT_EQ(info.GetName(), FilePath(FILE_PATH_LITERAL("..")));
614 CheckDirectoryAgainstInfo(info, dotdot);
615 found = true;
616 } else {
617 for (TestFile& file : files) {
618 if (info.GetName() == file.path.BaseName()) {
619 CheckFileAgainstInfo(info, file);
620 found = true;
621 break;
622 }
623 }
624 }
625
626 EXPECT_TRUE(found) << "Got unexpected result " << info.GetName().value();
627 }
628
629 EXPECT_TRUE(dotdot.found) << "Directory .. was not returned";
630
631 for (const TestFile& file : files) {
632 EXPECT_TRUE(file.found)
633 << "File " << file.path.value() << " was not returned";
634 }
635 }
636 #endif // !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN)
637
TEST(FileEnumerator,OnlyName)638 TEST(FileEnumerator, OnlyName) {
639 ScopedTempDir temp_dir;
640 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
641
642 const FilePath& path = temp_dir.GetPath();
643
644 // Add a directory and a file.
645 ScopedTempDir temp_subdir;
646 ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
647 const FilePath& subdir = temp_subdir.GetPath();
648 const FilePath dummy_file = path.AppendASCII("a_file.txt");
649 ASSERT_TRUE(CreateDummyFile(dummy_file));
650
651 auto found_paths = RunEnumerator(
652 path, /*recursive=*/false, FileEnumerator::FileType::NAMES_ONLY,
653 FilePath::StringType(), FileEnumerator::FolderSearchPolicy::MATCH_ONLY);
654 EXPECT_THAT(found_paths, UnorderedElementsAre(subdir, dummy_file));
655 }
656
657 struct FileEnumeratorForEachTestCase {
658 const bool recursive;
659 const int file_type;
660 const int expected_invocation_count;
661 };
662
663 class FileEnumeratorForEachTest
664 : public ::testing::TestWithParam<FileEnumeratorForEachTestCase> {};
665
666 INSTANTIATE_TEST_SUITE_P(
667 FileEnumeratorForEachTestCases,
668 FileEnumeratorForEachTest,
669 ::testing::ValuesIn(std::vector<FileEnumeratorForEachTestCase>{
670 {false, FileEnumerator::FILES, 2},
671 {true, FileEnumerator::FILES, 8},
672 {false, FileEnumerator::DIRECTORIES, 3},
673 {true, FileEnumerator::DIRECTORIES, 3},
674 {false, FileEnumerator::FILES | FileEnumerator::DIRECTORIES, 5},
675 {true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES, 11},
676 }));
677
TEST_P(FileEnumeratorForEachTest,TestCases)678 TEST_P(FileEnumeratorForEachTest, TestCases) {
679 ScopedTempDir temp_dir;
680 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
681 const FilePath mock_path(temp_dir.GetPath());
682
683 // Create a top-level directory, and 3 sub-directories, with 2 files within
684 // each directory.
685 for (const FilePath& path :
686 {mock_path, mock_path.Append(FILE_PATH_LITERAL("1.2.3.4")),
687 mock_path.Append(FILE_PATH_LITERAL("Download")),
688 mock_path.Append(FILE_PATH_LITERAL("Install"))}) {
689 ASSERT_TRUE(CreateDirectory(path));
690 for (const FilePath::StringType& file_name :
691 {FILE_PATH_LITERAL("mock.executable"),
692 FILE_PATH_LITERAL("mock.text")}) {
693 ASSERT_TRUE(
694 File(path.Append(file_name), File::FLAG_CREATE | File::FLAG_WRITE)
695 .IsValid());
696 }
697 }
698
699 int invocation_count = 0;
700
701 FileEnumerator(mock_path, GetParam().recursive, GetParam().file_type)
702 .ForEach([&invocation_count](const FilePath& item) {
703 ++invocation_count;
704 if (invocation_count > GetParam().expected_invocation_count) {
705 ADD_FAILURE() << "Unexpected file/directory found: " << item << ": "
706 << invocation_count << ": "
707 << GetParam().expected_invocation_count;
708 }
709 });
710
711 EXPECT_EQ(invocation_count, GetParam().expected_invocation_count);
712 }
713
714 } // namespace base
715