• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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