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