Add recursive pattern matching for subfolders in file_enumerator.
FolderSearchPolicy parameter is added to FileEnumerator class.
MATCH_ONLY policy is default and refers to the current behaviour where
the pattern only mathces the contents of root_path, not files in
recursive subdirectories. ALL policy is the new fixed behaviour. It
matches pattern to all subdirecroties.
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.win:win10_chromium_x64_rel_ng
Review-Url: https://codereview.chromium.org/2892173003
Cr-Commit-Position: refs/heads/master@{#483920}
CrOS-Libchrome-Original-Commit: 829f2e0b37d0175ac65aee0cbc9a603fcc994db7
diff --git a/base/files/file_enumerator.cc b/base/files/file_enumerator.cc
index 9749980..dfa277a 100644
--- a/base/files/file_enumerator.cc
+++ b/base/files/file_enumerator.cc
@@ -18,4 +18,9 @@
!(INCLUDE_DOT_DOT & file_type_));
}
+bool FileEnumerator::IsTypeMatched(bool is_dir) const {
+ return (file_type_ &
+ (is_dir ? FileEnumerator::DIRECTORIES : FileEnumerator::FILES)) != 0;
+}
+
} // namespace base
diff --git a/base/files/file_enumerator.h b/base/files/file_enumerator.h
index 7cac8dd..4f3ee57 100644
--- a/base/files/file_enumerator.h
+++ b/base/files/file_enumerator.h
@@ -84,6 +84,17 @@
#endif
};
+ // Search policy for intermediate folders.
+ enum class FolderSearchPolicy {
+ // Recursive search will pass through folders whose names match the
+ // pattern. Inside each one, all files will be returned. Folders with names
+ // that do not match the pattern will be ignored within their interior.
+ MATCH_ONLY,
+ // Recursive search will pass through every folder and perform pattern
+ // matching inside each one.
+ ALL,
+ };
+
// |root_path| is the starting directory to search for. It may or may not end
// in a slash.
//
@@ -101,9 +112,6 @@
// since the underlying code uses OS-specific matching routines. In general,
// Windows matching is less featureful than others, so test there first.
// If unspecified, this will match all files.
- // NOTE: the pattern only matches the contents of root_path, not files in
- // recursive subdirectories.
- // TODO(erikkay): Fix the pattern matching to work at all levels.
FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type);
@@ -111,6 +119,11 @@
bool recursive,
int file_type,
const FilePath::StringType& pattern);
+ FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern,
+ FolderSearchPolicy folder_search_policy);
~FileEnumerator();
// Returns the next file or an empty string if there are no more results.
@@ -127,28 +140,27 @@
// Returns true if the given path should be skipped in enumeration.
bool ShouldSkip(const FilePath& path);
+ bool IsTypeMatched(bool is_dir) const;
+
+ bool IsPatternMatched(const FilePath& src) const;
+
#if defined(OS_WIN)
// True when find_data_ is valid.
- bool has_find_data_;
+ bool has_find_data_ = false;
WIN32_FIND_DATA find_data_;
- HANDLE find_handle_;
+ HANDLE find_handle_ = INVALID_HANDLE_VALUE;
#elif defined(OS_POSIX)
-
- // Read the filenames in source into the vector of DirectoryEntryInfo's
- static bool ReadDirectory(std::vector<FileInfo>* entries,
- const FilePath& source, bool show_links);
-
// The files in the current directory
std::vector<FileInfo> directory_entries_;
// The next entry to use from the directory_entries_ vector
size_t current_directory_entry_;
#endif
-
FilePath root_path_;
- bool recursive_;
- int file_type_;
- FilePath::StringType pattern_; // Empty when we want to find everything.
+ const bool recursive_;
+ const int file_type_;
+ FilePath::StringType pattern_;
+ const FolderSearchPolicy folder_search_policy_;
// A stack that keeps track of which subdirectories we still need to
// enumerate in the breadth-first search.
diff --git a/base/files/file_enumerator_posix.cc b/base/files/file_enumerator_posix.cc
index a1b4949..bc5c3cc 100644
--- a/base/files/file_enumerator_posix.cc
+++ b/base/files/file_enumerator_posix.cc
@@ -8,12 +8,29 @@
#include <errno.h>
#include <fnmatch.h>
#include <stdint.h>
+#include <string.h>
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
namespace base {
+namespace {
+
+void GetStat(const FilePath& path, bool show_links, struct stat* st) {
+ DCHECK(st);
+ const int res = show_links ? lstat(path.value().c_str(), st)
+ : stat(path.value().c_str(), st);
+ if (res < 0) {
+ // Print the stat() error message unless it was ENOENT and we're following
+ // symlinks.
+ if (!(errno == ENOENT && !show_links))
+ DPLOG(ERROR) << "Couldn't stat" << path.value();
+ memset(st, 0, sizeof(*st));
+ }
+}
+
+} // namespace
// FileEnumerator::FileInfo ----------------------------------------------------
@@ -42,31 +59,36 @@
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type)
- : current_directory_entry_(0),
- root_path_(root_path),
- recursive_(recursive),
- file_type_(file_type) {
- // INCLUDE_DOT_DOT must not be specified if recursive.
- DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
- pending_paths_.push(root_path);
-}
+ : FileEnumerator(root_path,
+ recursive,
+ file_type,
+ FilePath::StringType(),
+ FolderSearchPolicy::MATCH_ONLY) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern)
+ : FileEnumerator(root_path,
+ recursive,
+ file_type,
+ pattern,
+ FolderSearchPolicy::MATCH_ONLY) {}
+
+FileEnumerator::FileEnumerator(const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern,
+ FolderSearchPolicy folder_search_policy)
: current_directory_entry_(0),
root_path_(root_path),
recursive_(recursive),
file_type_(file_type),
- pattern_(root_path.Append(pattern).value()) {
+ pattern_(pattern),
+ folder_search_policy_(folder_search_policy) {
// INCLUDE_DOT_DOT must not be specified if recursive.
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
- // The Windows version of this code appends the pattern to the root_path,
- // potentially only matching against items in the top-most directory.
- // Do the same here.
- if (pattern.empty())
- pattern_ = FilePath::StringType();
+
pending_paths_.push(root_path);
}
@@ -74,6 +96,8 @@
}
FilePath FileEnumerator::Next() {
+ base::ThreadRestrictions::AssertIOAllowed();
+
++current_directory_entry_;
// While we've exhausted the entries in the current directory, do the next
@@ -85,29 +109,66 @@
root_path_ = root_path_.StripTrailingSeparators();
pending_paths_.pop();
- std::vector<FileInfo> entries;
- if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS))
+ DIR* dir = opendir(root_path_.value().c_str());
+ if (!dir)
continue;
directory_entries_.clear();
+
+#if defined(OS_FUCHSIA)
+ // Fuchsia does not support .. on the file system server side, see
+ // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
+ // https://crbug.com/735540. However, for UI purposes, having the parent
+ // directory show up in directory listings makes sense, so we add it here to
+ // match the expectation on other operating systems. In cases where this
+ // is useful it should be resolvable locally.
+ FileInfo dotdot;
+ dotdot.stat_.st_mode = S_IFDIR;
+ dotdot.filename_ = FilePath("..");
+ directory_entries_->push_back(dotdot);
+#endif // OS_FUCHSIA
+
current_directory_entry_ = 0;
- for (std::vector<FileInfo>::const_iterator i = entries.begin();
- i != entries.end(); ++i) {
- FilePath full_path = root_path_.Append(i->filename_);
- if (ShouldSkip(full_path))
+ struct dirent* dent;
+ while ((dent = readdir(dir))) {
+ FileInfo info;
+ info.filename_ = FilePath(dent->d_name);
+
+ if (ShouldSkip(info.filename_))
continue;
- if (pattern_.size() &&
- fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE))
+ const bool is_pattern_matched = IsPatternMatched(info.filename_);
+
+ // MATCH_ONLY policy enumerates files and directories which matching
+ // pattern only. So we can early skip further checks.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
+ !is_pattern_matched)
continue;
- if (recursive_ && S_ISDIR(i->stat_.st_mode))
+ // Do not call OS stat/lstat if there is no sense to do it. If pattern is
+ // not matched (file will not appear in results) and search is not
+ // recursive (possible directory will not be added to pending paths) -
+ // there is no sense to obtain item below.
+ if (!recursive_ && !is_pattern_matched)
+ continue;
+
+ const FilePath full_path = root_path_.Append(info.filename_);
+ GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_);
+
+ const bool is_dir = S_ISDIR(info.stat_.st_mode);
+
+ if (recursive_ && is_dir)
pending_paths_.push(full_path);
- if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) ||
- (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES)))
- directory_entries_.push_back(*i);
+ if (is_pattern_matched && IsTypeMatched(is_dir))
+ directory_entries_.push_back(std::move(info));
}
+ closedir(dir);
+
+ // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
+ // ALL policy enumerates files in all subfolders by origin pattern.
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
+ pattern_.clear();
}
return root_path_.Append(
@@ -118,51 +179,9 @@
return directory_entries_[current_directory_entry_];
}
-bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries,
- const FilePath& source, bool show_links) {
- base::ThreadRestrictions::AssertIOAllowed();
- DIR* dir = opendir(source.value().c_str());
- if (!dir)
- return false;
-
-#if defined(OS_FUCHSIA)
- // Fuchsia does not support .. on the file system server side, see
- // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
- // https://crbug.com/735540. However, for UI purposes, having the parent
- // directory show up in directory listings makes sense, so we add it here to
- // match the expectation on other operating systems. In cases where this
- // is useful it should be resolvable locally.
- FileInfo dotdot;
- dotdot.stat_.st_mode = S_IFDIR;
- dotdot.filename_ = FilePath("..");
- entries->push_back(dotdot);
-#endif // OS_FUCHSIA
-
- struct dirent* dent;
- while ((dent = readdir(dir))) {
- FileInfo info;
- info.filename_ = FilePath(dent->d_name);
-
- FilePath full_name = source.Append(dent->d_name);
- int ret;
- if (show_links)
- ret = lstat(full_name.value().c_str(), &info.stat_);
- else
- ret = stat(full_name.value().c_str(), &info.stat_);
- if (ret < 0) {
- // Print the stat() error message unless it was ENOENT and we're
- // following symlinks.
- if (!(errno == ENOENT && !show_links)) {
- DPLOG(ERROR) << "Couldn't stat "
- << source.Append(dent->d_name).value();
- }
- memset(&info.stat_, 0, sizeof(info.stat_));
- }
- entries->push_back(info);
- }
-
- closedir(dir);
- return true;
+bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
+ return pattern_.empty() ||
+ !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
}
} // namespace base
diff --git a/base/files/file_enumerator_unittest.cc b/base/files/file_enumerator_unittest.cc
new file mode 100644
index 0000000..5faa80f
--- /dev/null
+++ b/base/files/file_enumerator_unittest.cc
@@ -0,0 +1,312 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_enumerator.h"
+
+#include <deque>
+#include <utility>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::UnorderedElementsAre;
+
+namespace base {
+namespace {
+
+const FilePath::StringType kEmptyPattern;
+
+const std::vector<FileEnumerator::FolderSearchPolicy> kFolderSearchPolicies{
+ FileEnumerator::FolderSearchPolicy::MATCH_ONLY,
+ FileEnumerator::FolderSearchPolicy::ALL};
+
+std::deque<FilePath> RunEnumerator(
+ const FilePath& root_path,
+ bool recursive,
+ int file_type,
+ const FilePath::StringType& pattern,
+ FileEnumerator::FolderSearchPolicy folder_search_policy) {
+ std::deque<FilePath> rv;
+ FileEnumerator enumerator(root_path, recursive, file_type, pattern,
+ folder_search_policy);
+ for (auto file = enumerator.Next(); !file.empty(); file = enumerator.Next())
+ rv.emplace_back(std::move(file));
+ return rv;
+}
+
+bool CreateDummyFile(const FilePath& path) {
+ return WriteFile(path, "42", sizeof("42")) == sizeof("42");
+}
+
+} // namespace
+
+TEST(FileEnumerator, NotExistingPath) {
+ const FilePath path = FilePath::FromUTF8Unsafe("some_not_existing_path");
+ ASSERT_FALSE(PathExists(path));
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files = RunEnumerator(
+ path, true, FileEnumerator::FILES & FileEnumerator::DIRECTORIES,
+ FILE_PATH_LITERAL(""), policy);
+ EXPECT_THAT(files, IsEmpty());
+ }
+}
+
+TEST(FileEnumerator, EmptyFolder) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files =
+ RunEnumerator(temp_dir.GetPath(), true,
+ FileEnumerator::FILES & FileEnumerator::DIRECTORIES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, IsEmpty());
+ }
+}
+
+TEST(FileEnumerator, SingleFileInFolderForFileSearch) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+ const FilePath file = path.AppendASCII("test.txt");
+ ASSERT_TRUE(CreateDummyFile(file));
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files = RunEnumerator(
+ temp_dir.GetPath(), true, FileEnumerator::FILES, kEmptyPattern, policy);
+ EXPECT_THAT(files, ElementsAre(file));
+ }
+}
+
+TEST(FileEnumerator, SingleFileInFolderForDirSearch) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+ ASSERT_TRUE(CreateDummyFile(path.AppendASCII("test.txt")));
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, IsEmpty());
+ }
+}
+
+TEST(FileEnumerator, SingleFileInFolderWithFiltering) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+ const FilePath file = path.AppendASCII("test.txt");
+ ASSERT_TRUE(CreateDummyFile(file));
+
+ for (auto policy : kFolderSearchPolicies) {
+ auto files = RunEnumerator(path, true, FileEnumerator::FILES,
+ FILE_PATH_LITERAL("*.txt"), policy);
+ EXPECT_THAT(files, ElementsAre(file));
+
+ files = RunEnumerator(path, true, FileEnumerator::FILES,
+ FILE_PATH_LITERAL("*.pdf"), policy);
+ EXPECT_THAT(files, IsEmpty());
+ }
+}
+
+TEST(FileEnumerator, TwoFilesInFolder) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+ const FilePath foo_txt = path.AppendASCII("foo.txt");
+ const FilePath bar_txt = path.AppendASCII("bar.txt");
+ ASSERT_TRUE(CreateDummyFile(foo_txt));
+ ASSERT_TRUE(CreateDummyFile(bar_txt));
+
+ for (auto policy : kFolderSearchPolicies) {
+ auto files = RunEnumerator(path, true, FileEnumerator::FILES,
+ FILE_PATH_LITERAL("*.txt"), policy);
+ EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt));
+
+ files = RunEnumerator(path, true, FileEnumerator::FILES,
+ FILE_PATH_LITERAL("foo*"), policy);
+ EXPECT_THAT(files, ElementsAre(foo_txt));
+
+ files = RunEnumerator(path, true, FileEnumerator::FILES,
+ FILE_PATH_LITERAL("*.pdf"), policy);
+ EXPECT_THAT(files, IsEmpty());
+
+ files =
+ RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
+ EXPECT_THAT(files, UnorderedElementsAre(foo_txt, bar_txt));
+ }
+}
+
+TEST(FileEnumerator, SingleFolderInFolderForFileSearch) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+
+ ScopedTempDir temp_subdir;
+ ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files =
+ RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
+ EXPECT_THAT(files, IsEmpty());
+ }
+}
+
+TEST(FileEnumerator, SingleFolderInFolderForDirSearch) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+
+ ScopedTempDir temp_subdir;
+ ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath()));
+ }
+}
+
+TEST(FileEnumerator, TwoFoldersInFolder) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+
+ const FilePath subdir_foo = path.AppendASCII("foo");
+ const FilePath subdir_bar = path.AppendASCII("bar");
+ ASSERT_TRUE(CreateDirectory(subdir_foo));
+ ASSERT_TRUE(CreateDirectory(subdir_bar));
+
+ for (auto policy : kFolderSearchPolicies) {
+ auto files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, subdir_bar));
+
+ files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
+ FILE_PATH_LITERAL("foo"), policy);
+ EXPECT_THAT(files, ElementsAre(subdir_foo));
+ }
+}
+
+TEST(FileEnumerator, FolderAndFileInFolder) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+
+ ScopedTempDir temp_subdir;
+ ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
+ const FilePath file = path.AppendASCII("test.txt");
+ ASSERT_TRUE(CreateDummyFile(file));
+
+ for (auto policy : kFolderSearchPolicies) {
+ auto files =
+ RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
+ EXPECT_THAT(files, ElementsAre(file));
+
+ files = RunEnumerator(path, true, FileEnumerator::DIRECTORIES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, ElementsAre(temp_subdir.GetPath()));
+
+ files = RunEnumerator(path, true,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, UnorderedElementsAre(file, temp_subdir.GetPath()));
+ }
+}
+
+TEST(FileEnumerator, FilesInParentFolderAlwaysFirst) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath& path = temp_dir.GetPath();
+
+ ScopedTempDir temp_subdir;
+ ASSERT_TRUE(temp_subdir.CreateUniqueTempDirUnderPath(path));
+ const FilePath foo_txt = path.AppendASCII("foo.txt");
+ const FilePath bar_txt = temp_subdir.GetPath().AppendASCII("bar.txt");
+ ASSERT_TRUE(CreateDummyFile(foo_txt));
+ ASSERT_TRUE(CreateDummyFile(bar_txt));
+
+ for (auto policy : kFolderSearchPolicies) {
+ const auto files =
+ RunEnumerator(path, true, FileEnumerator::FILES, kEmptyPattern, policy);
+ EXPECT_THAT(files, ElementsAre(foo_txt, bar_txt));
+ }
+}
+
+TEST(FileEnumerator, FileInSubfolder) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath subdir = temp_dir.GetPath().AppendASCII("subdir");
+ ASSERT_TRUE(CreateDirectory(subdir));
+
+ const FilePath file = subdir.AppendASCII("test.txt");
+ ASSERT_TRUE(CreateDummyFile(file));
+
+ for (auto policy : kFolderSearchPolicies) {
+ auto files = RunEnumerator(temp_dir.GetPath(), true, FileEnumerator::FILES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, ElementsAre(file));
+
+ files = RunEnumerator(temp_dir.GetPath(), false, FileEnumerator::FILES,
+ kEmptyPattern, policy);
+ EXPECT_THAT(files, IsEmpty());
+ }
+}
+
+TEST(FileEnumerator, FilesInSubfoldersWithFiltering) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ const FilePath test_txt = temp_dir.GetPath().AppendASCII("test.txt");
+ const FilePath subdir_foo = temp_dir.GetPath().AppendASCII("foo_subdir");
+ const FilePath subdir_bar = temp_dir.GetPath().AppendASCII("bar_subdir");
+ const FilePath foo_test = subdir_foo.AppendASCII("test.txt");
+ const FilePath foo_foo = subdir_foo.AppendASCII("foo.txt");
+ const FilePath foo_bar = subdir_foo.AppendASCII("bar.txt");
+ const FilePath bar_test = subdir_bar.AppendASCII("test.txt");
+ const FilePath bar_foo = subdir_bar.AppendASCII("foo.txt");
+ const FilePath bar_bar = subdir_bar.AppendASCII("bar.txt");
+ ASSERT_TRUE(CreateDummyFile(test_txt));
+ ASSERT_TRUE(CreateDirectory(subdir_foo));
+ ASSERT_TRUE(CreateDirectory(subdir_bar));
+ ASSERT_TRUE(CreateDummyFile(foo_test));
+ ASSERT_TRUE(CreateDummyFile(foo_foo));
+ ASSERT_TRUE(CreateDummyFile(foo_bar));
+ ASSERT_TRUE(CreateDummyFile(bar_test));
+ ASSERT_TRUE(CreateDummyFile(bar_foo));
+ ASSERT_TRUE(CreateDummyFile(bar_bar));
+
+ auto files =
+ RunEnumerator(temp_dir.GetPath(), true,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
+ FILE_PATH_LITERAL("foo*"),
+ FileEnumerator::FolderSearchPolicy::MATCH_ONLY);
+ EXPECT_THAT(files,
+ UnorderedElementsAre(subdir_foo, foo_test, foo_foo, foo_bar));
+
+ files = RunEnumerator(temp_dir.GetPath(), true,
+ FileEnumerator::FILES | FileEnumerator::DIRECTORIES,
+ FILE_PATH_LITERAL("foo*"),
+ FileEnumerator::FolderSearchPolicy::ALL);
+ EXPECT_THAT(files, UnorderedElementsAre(subdir_foo, foo_foo, bar_foo));
+}
+
+} // namespace base