xref: /aosp_15_r20/external/cronet/base/files/file_enumerator_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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