| // Copyright (c) 2012 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 <list> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/i18n/file_util_icu.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "net/base/directory_lister.h" |
| #include "net/base/net_errors.h" |
| #include "net/test/gtest_util.h" |
| #include "net/test/test_with_scoped_task_environment.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/platform_test.h" |
| |
| using net::test::IsError; |
| using net::test::IsOk; |
| |
| namespace net { |
| |
| namespace { |
| |
| const int kMaxDepth = 3; |
| const int kBranchingFactor = 4; |
| const int kFilesPerDirectory = 5; |
| |
| class ListerDelegate : public DirectoryLister::DirectoryListerDelegate { |
| public: |
| explicit ListerDelegate(DirectoryLister::ListingType type) |
| : cancel_lister_on_list_file_(false), |
| cancel_lister_on_list_done_(false), |
| lister_(nullptr), |
| done_(false), |
| error_(-1), |
| type_(type) {} |
| |
| // When set to true, this signals that the directory list operation should be |
| // cancelled (And the run loop quit) in the first call to OnListFile. |
| void set_cancel_lister_on_list_file(bool cancel_lister_on_list_file) { |
| cancel_lister_on_list_file_ = cancel_lister_on_list_file; |
| } |
| |
| // When set to true, this signals that the directory list operation should be |
| // cancelled (And the run loop quit) when OnDone is called. |
| void set_cancel_lister_on_list_done(bool cancel_lister_on_list_done) { |
| cancel_lister_on_list_done_ = cancel_lister_on_list_done; |
| } |
| |
| void OnListFile(const DirectoryLister::DirectoryListerData& data) override { |
| ASSERT_FALSE(done_); |
| |
| file_list_.push_back(data.info); |
| paths_.push_back(data.path); |
| if (cancel_lister_on_list_file_) { |
| lister_->Cancel(); |
| run_loop.Quit(); |
| } |
| } |
| |
| void OnListDone(int error) override { |
| ASSERT_FALSE(done_); |
| |
| done_ = true; |
| error_ = error; |
| if (type_ == DirectoryLister::ALPHA_DIRS_FIRST) |
| CheckSort(); |
| |
| if (cancel_lister_on_list_done_) |
| lister_->Cancel(); |
| run_loop.Quit(); |
| } |
| |
| void CheckSort() { |
| // Check that we got files in the right order. |
| if (!file_list_.empty()) { |
| for (size_t previous = 0, current = 1; |
| current < file_list_.size(); |
| previous++, current++) { |
| // Directories should come before files. |
| if (file_list_[previous].IsDirectory() && |
| !file_list_[current].IsDirectory()) { |
| continue; |
| } |
| EXPECT_NE(FILE_PATH_LITERAL(".."), |
| file_list_[current].GetName().BaseName().value()); |
| EXPECT_EQ(file_list_[previous].IsDirectory(), |
| file_list_[current].IsDirectory()); |
| #if !defined(STARBOARD) |
| EXPECT_TRUE(base::i18n::LocaleAwareCompareFilenames( |
| file_list_[previous].GetName(), |
| file_list_[current].GetName())); |
| #endif |
| } |
| } |
| } |
| |
| void Run(DirectoryLister* lister) { |
| lister_ = lister; |
| lister_->Start(); |
| run_loop.Run(); |
| } |
| |
| int error() const { return error_; } |
| |
| int num_files() const { return file_list_.size(); } |
| |
| bool done() const { return done_; } |
| |
| private: |
| bool cancel_lister_on_list_file_; |
| bool cancel_lister_on_list_done_; |
| |
| // This is owned by the individual tests, rather than the ListerDelegate. |
| DirectoryLister* lister_; |
| |
| base::RunLoop run_loop; |
| |
| bool done_; |
| int error_; |
| DirectoryLister::ListingType type_; |
| |
| std::vector<base::FileEnumerator::FileInfo> file_list_; |
| std::vector<base::FilePath> paths_; |
| }; |
| |
| } // namespace |
| |
| class DirectoryListerTest : public PlatformTest, |
| public WithScopedTaskEnvironment { |
| public: |
| DirectoryListerTest() |
| : total_created_file_system_objects_in_temp_root_dir_(0), |
| created_file_system_objects_in_temp_root_dir_(0) {} |
| |
| void SetUp() override { |
| // Randomly create a directory structure of depth 3 in a temporary root |
| // directory. |
| std::list<std::pair<base::FilePath, int> > directories; |
| ASSERT_TRUE(temp_root_dir_.CreateUniqueTempDir()); |
| directories.push_back(std::make_pair(temp_root_dir_.GetPath(), 0)); |
| while (!directories.empty()) { |
| std::pair<base::FilePath, int> dir_data = directories.front(); |
| directories.pop_front(); |
| for (int i = 0; i < kFilesPerDirectory; i++) { |
| std::string file_name = base::StringPrintf("file_id_%d", i); |
| base::FilePath file_path = dir_data.first.AppendASCII(file_name); |
| base::File file(file_path, |
| base::File::FLAG_CREATE | base::File::FLAG_WRITE); |
| ASSERT_TRUE(file.IsValid()); |
| ++total_created_file_system_objects_in_temp_root_dir_; |
| if (dir_data.first == temp_root_dir_.GetPath()) |
| ++created_file_system_objects_in_temp_root_dir_; |
| } |
| if (dir_data.second < kMaxDepth - 1) { |
| for (int i = 0; i < kBranchingFactor; i++) { |
| std::string dir_name = base::StringPrintf("child_dir_%d", i); |
| base::FilePath dir_path = dir_data.first.AppendASCII(dir_name); |
| ASSERT_TRUE(base::CreateDirectory(dir_path)); |
| ++total_created_file_system_objects_in_temp_root_dir_; |
| if (dir_data.first == temp_root_dir_.GetPath()) |
| ++created_file_system_objects_in_temp_root_dir_; |
| directories.push_back(std::make_pair(dir_path, dir_data.second + 1)); |
| } |
| } |
| } |
| PlatformTest::SetUp(); |
| } |
| |
| const base::FilePath& root_path() const { return temp_root_dir_.GetPath(); } |
| |
| int expected_list_length_recursive() const { |
| // List should include everything but the top level directory, and does not |
| // include "..". |
| return total_created_file_system_objects_in_temp_root_dir_; |
| } |
| |
| int expected_list_length_non_recursive() const { |
| // List should include everything in the top level directory, and "..". |
| return created_file_system_objects_in_temp_root_dir_ + 1; |
| } |
| |
| private: |
| // Number of files and directories created in SetUp, excluding |
| // |temp_root_dir_| itself. Includes all nested directories and their files. |
| int total_created_file_system_objects_in_temp_root_dir_; |
| // Number of files and directories created directly in |temp_root_dir_|. |
| int created_file_system_objects_in_temp_root_dir_; |
| |
| base::ScopedTempDir temp_root_dir_; |
| }; |
| |
| TEST_F(DirectoryListerTest, BigDirTest) { |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| DirectoryLister lister(root_path(), &delegate); |
| delegate.Run(&lister); |
| |
| EXPECT_TRUE(delegate.done()); |
| EXPECT_THAT(delegate.error(), IsOk()); |
| EXPECT_EQ(expected_list_length_non_recursive(), delegate.num_files()); |
| } |
| |
| TEST_F(DirectoryListerTest, BigDirRecursiveTest) { |
| ListerDelegate delegate(DirectoryLister::NO_SORT_RECURSIVE); |
| DirectoryLister lister(root_path(), DirectoryLister::NO_SORT_RECURSIVE, |
| &delegate); |
| delegate.Run(&lister); |
| |
| EXPECT_TRUE(delegate.done()); |
| EXPECT_THAT(delegate.error(), IsOk()); |
| EXPECT_EQ(expected_list_length_recursive(), delegate.num_files()); |
| } |
| |
| TEST_F(DirectoryListerTest, EmptyDirTest) { |
| base::ScopedTempDir tempDir; |
| EXPECT_TRUE(tempDir.CreateUniqueTempDir()); |
| |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| DirectoryLister lister(tempDir.GetPath(), &delegate); |
| delegate.Run(&lister); |
| |
| EXPECT_TRUE(delegate.done()); |
| EXPECT_THAT(delegate.error(), IsOk()); |
| // Contains only the parent directory (".."). |
| EXPECT_EQ(1, delegate.num_files()); |
| } |
| |
| // This doesn't really test much, except make sure calling cancel before any |
| // callbacks are invoked doesn't crash. Can't wait for all tasks running on a |
| // worker pool to complete, unfortunately. |
| // TODO(mmenke): See if there's a way to make this fail more reliably on |
| // regression. |
| TEST_F(DirectoryListerTest, BasicCancelTest) { |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| std::unique_ptr<DirectoryLister> lister( |
| new DirectoryLister(root_path(), &delegate)); |
| lister->Start(); |
| lister->Cancel(); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(delegate.done()); |
| EXPECT_EQ(0, delegate.num_files()); |
| } |
| |
| TEST_F(DirectoryListerTest, CancelOnListFileTest) { |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| DirectoryLister lister(root_path(), &delegate); |
| delegate.set_cancel_lister_on_list_file(true); |
| delegate.Run(&lister); |
| |
| EXPECT_FALSE(delegate.done()); |
| EXPECT_EQ(1, delegate.num_files()); |
| } |
| |
| TEST_F(DirectoryListerTest, CancelOnListDoneTest) { |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| DirectoryLister lister(root_path(), &delegate); |
| delegate.set_cancel_lister_on_list_done(true); |
| delegate.Run(&lister); |
| |
| EXPECT_TRUE(delegate.done()); |
| EXPECT_THAT(delegate.error(), IsOk()); |
| EXPECT_EQ(expected_list_length_non_recursive(), delegate.num_files()); |
| } |
| |
| TEST_F(DirectoryListerTest, CancelOnLastElementTest) { |
| base::ScopedTempDir tempDir; |
| EXPECT_TRUE(tempDir.CreateUniqueTempDir()); |
| |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| DirectoryLister lister(tempDir.GetPath(), &delegate); |
| delegate.set_cancel_lister_on_list_file(true); |
| delegate.Run(&lister); |
| |
| EXPECT_FALSE(delegate.done()); |
| // Contains only the parent directory (".."). |
| EXPECT_EQ(1, delegate.num_files()); |
| } |
| |
| TEST_F(DirectoryListerTest, NoSuchDirTest) { |
| base::ScopedTempDir tempDir; |
| EXPECT_TRUE(tempDir.CreateUniqueTempDir()); |
| |
| ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST); |
| DirectoryLister lister( |
| tempDir.GetPath().AppendASCII("this_path_does_not_exist"), &delegate); |
| delegate.Run(&lister); |
| |
| EXPECT_THAT(delegate.error(), IsError(ERR_FILE_NOT_FOUND)); |
| EXPECT_EQ(0, delegate.num_files()); |
| } |
| |
| } // namespace net |