xref: /aosp_15_r20/external/cronet/net/base/directory_lister_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 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 <list>
6 #include <utility>
7 #include <vector>
8 
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/functional/bind.h"
13 #include "base/i18n/file_util_icu.h"
14 #include "base/memory/raw_ptr.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "net/base/directory_lister.h"
18 #include "net/base/net_errors.h"
19 #include "net/test/gtest_util.h"
20 #include "net/test/test_with_task_environment.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "testing/platform_test.h"
24 
25 using net::test::IsError;
26 using net::test::IsOk;
27 
28 namespace net {
29 
30 namespace {
31 
32 const int kMaxDepth = 3;
33 const int kBranchingFactor = 4;
34 const int kFilesPerDirectory = 5;
35 
36 class ListerDelegate : public DirectoryLister::DirectoryListerDelegate {
37  public:
ListerDelegate(DirectoryLister::ListingType type)38   explicit ListerDelegate(DirectoryLister::ListingType type) : type_(type) {}
39 
40   // When set to true, this signals that the directory list operation should be
41   // cancelled (And the run loop quit) in the first call to OnListFile.
set_cancel_lister_on_list_file(bool cancel_lister_on_list_file)42   void set_cancel_lister_on_list_file(bool cancel_lister_on_list_file) {
43     cancel_lister_on_list_file_ = cancel_lister_on_list_file;
44   }
45 
46   // When set to true, this signals that the directory list operation should be
47   // cancelled (And the run loop quit) when OnDone is called.
set_cancel_lister_on_list_done(bool cancel_lister_on_list_done)48   void set_cancel_lister_on_list_done(bool cancel_lister_on_list_done) {
49     cancel_lister_on_list_done_ = cancel_lister_on_list_done;
50   }
51 
OnListFile(const DirectoryLister::DirectoryListerData & data)52   void OnListFile(const DirectoryLister::DirectoryListerData& data) override {
53     ASSERT_FALSE(done_);
54 
55     file_list_.push_back(data.info);
56     paths_.push_back(data.path);
57     if (cancel_lister_on_list_file_) {
58       lister_->Cancel();
59       run_loop.Quit();
60     }
61   }
62 
OnListDone(int error)63   void OnListDone(int error) override {
64     ASSERT_FALSE(done_);
65 
66     done_ = true;
67     error_ = error;
68     if (type_ == DirectoryLister::ALPHA_DIRS_FIRST)
69       CheckSort();
70 
71     if (cancel_lister_on_list_done_)
72       lister_->Cancel();
73     run_loop.Quit();
74   }
75 
CheckSort()76   void CheckSort() {
77     // Check that we got files in the right order.
78     if (!file_list_.empty()) {
79       for (size_t previous = 0, current = 1;
80            current < file_list_.size();
81            previous++, current++) {
82         // Directories should come before files.
83         if (file_list_[previous].IsDirectory() &&
84             !file_list_[current].IsDirectory()) {
85           continue;
86         }
87         EXPECT_NE(FILE_PATH_LITERAL(".."),
88                   file_list_[current].GetName().BaseName().value());
89         EXPECT_EQ(file_list_[previous].IsDirectory(),
90                   file_list_[current].IsDirectory());
91         EXPECT_TRUE(base::i18n::LocaleAwareCompareFilenames(
92             file_list_[previous].GetName(),
93             file_list_[current].GetName()));
94       }
95     }
96   }
97 
Run(DirectoryLister * lister)98   void Run(DirectoryLister* lister) {
99     lister_ = lister;
100     lister_->Start();
101     run_loop.Run();
102   }
103 
error() const104   int error() const { return error_; }
105 
num_files() const106   int num_files() const { return file_list_.size(); }
107 
done() const108   bool done() const { return done_; }
109 
110  private:
111   bool cancel_lister_on_list_file_ = false;
112   bool cancel_lister_on_list_done_ = false;
113 
114   // This is owned by the individual tests, rather than the ListerDelegate.
115   raw_ptr<DirectoryLister> lister_ = nullptr;
116 
117   base::RunLoop run_loop;
118 
119   bool done_ = false;
120   int error_ = -1;
121   DirectoryLister::ListingType type_;
122 
123   std::vector<base::FileEnumerator::FileInfo> file_list_;
124   std::vector<base::FilePath> paths_;
125 };
126 
127 }  // namespace
128 
129 class DirectoryListerTest : public PlatformTest, public WithTaskEnvironment {
130  public:
131   DirectoryListerTest() = default;
132 
SetUp()133   void SetUp() override {
134     // Randomly create a directory structure of depth 3 in a temporary root
135     // directory.
136     std::list<std::pair<base::FilePath, int> > directories;
137     ASSERT_TRUE(temp_root_dir_.CreateUniqueTempDir());
138     directories.emplace_back(temp_root_dir_.GetPath(), 0);
139     while (!directories.empty()) {
140       std::pair<base::FilePath, int> dir_data = directories.front();
141       directories.pop_front();
142       for (int i = 0; i < kFilesPerDirectory; i++) {
143         std::string file_name = base::StringPrintf("file_id_%d", i);
144         base::FilePath file_path = dir_data.first.AppendASCII(file_name);
145         base::File file(file_path,
146                         base::File::FLAG_CREATE | base::File::FLAG_WRITE);
147         ASSERT_TRUE(file.IsValid());
148         ++total_created_file_system_objects_in_temp_root_dir_;
149         if (dir_data.first == temp_root_dir_.GetPath())
150           ++created_file_system_objects_in_temp_root_dir_;
151       }
152       if (dir_data.second < kMaxDepth - 1) {
153         for (int i = 0; i < kBranchingFactor; i++) {
154           std::string dir_name = base::StringPrintf("child_dir_%d", i);
155           base::FilePath dir_path = dir_data.first.AppendASCII(dir_name);
156           ASSERT_TRUE(base::CreateDirectory(dir_path));
157           ++total_created_file_system_objects_in_temp_root_dir_;
158           if (dir_data.first == temp_root_dir_.GetPath())
159             ++created_file_system_objects_in_temp_root_dir_;
160           directories.emplace_back(dir_path, dir_data.second + 1);
161         }
162       }
163     }
164     PlatformTest::SetUp();
165   }
166 
root_path() const167   const base::FilePath& root_path() const { return temp_root_dir_.GetPath(); }
168 
expected_list_length_recursive() const169   int expected_list_length_recursive() const {
170     // List should include everything but the top level directory, and does not
171     // include "..".
172     return total_created_file_system_objects_in_temp_root_dir_;
173   }
174 
expected_list_length_non_recursive() const175   int expected_list_length_non_recursive() const {
176     // List should include everything in the top level directory, and "..".
177     return created_file_system_objects_in_temp_root_dir_ + 1;
178   }
179 
180  private:
181   // Number of files and directories created in SetUp, excluding
182   // |temp_root_dir_| itself.  Includes all nested directories and their files.
183   int total_created_file_system_objects_in_temp_root_dir_ = 0;
184   // Number of files and directories created directly in |temp_root_dir_|.
185   int created_file_system_objects_in_temp_root_dir_ = 0;
186 
187   base::ScopedTempDir temp_root_dir_;
188 };
189 
TEST_F(DirectoryListerTest,BigDirTest)190 TEST_F(DirectoryListerTest, BigDirTest) {
191   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
192   DirectoryLister lister(root_path(), &delegate);
193   delegate.Run(&lister);
194 
195   EXPECT_TRUE(delegate.done());
196   EXPECT_THAT(delegate.error(), IsOk());
197   EXPECT_EQ(expected_list_length_non_recursive(), delegate.num_files());
198 }
199 
TEST_F(DirectoryListerTest,BigDirRecursiveTest)200 TEST_F(DirectoryListerTest, BigDirRecursiveTest) {
201   ListerDelegate delegate(DirectoryLister::NO_SORT_RECURSIVE);
202   DirectoryLister lister(root_path(), DirectoryLister::NO_SORT_RECURSIVE,
203                          &delegate);
204   delegate.Run(&lister);
205 
206   EXPECT_TRUE(delegate.done());
207   EXPECT_THAT(delegate.error(), IsOk());
208   EXPECT_EQ(expected_list_length_recursive(), delegate.num_files());
209 }
210 
TEST_F(DirectoryListerTest,EmptyDirTest)211 TEST_F(DirectoryListerTest, EmptyDirTest) {
212   base::ScopedTempDir tempDir;
213   EXPECT_TRUE(tempDir.CreateUniqueTempDir());
214 
215   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
216   DirectoryLister lister(tempDir.GetPath(), &delegate);
217   delegate.Run(&lister);
218 
219   EXPECT_TRUE(delegate.done());
220   EXPECT_THAT(delegate.error(), IsOk());
221   // Contains only the parent directory ("..").
222   EXPECT_EQ(1, delegate.num_files());
223 }
224 
225 // This doesn't really test much, except make sure calling cancel before any
226 // callbacks are invoked doesn't crash.  Can't wait for all tasks running on a
227 // worker pool to complete, unfortunately.
228 // TODO(mmenke):  See if there's a way to make this fail more reliably on
229 // regression.
TEST_F(DirectoryListerTest,BasicCancelTest)230 TEST_F(DirectoryListerTest, BasicCancelTest) {
231   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
232   auto lister = std::make_unique<DirectoryLister>(root_path(), &delegate);
233   lister->Start();
234   lister->Cancel();
235   base::RunLoop().RunUntilIdle();
236 
237   EXPECT_FALSE(delegate.done());
238   EXPECT_EQ(0, delegate.num_files());
239 }
240 
TEST_F(DirectoryListerTest,CancelOnListFileTest)241 TEST_F(DirectoryListerTest, CancelOnListFileTest) {
242   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
243   DirectoryLister lister(root_path(), &delegate);
244   delegate.set_cancel_lister_on_list_file(true);
245   delegate.Run(&lister);
246 
247   EXPECT_FALSE(delegate.done());
248   EXPECT_EQ(1, delegate.num_files());
249 }
250 
TEST_F(DirectoryListerTest,CancelOnListDoneTest)251 TEST_F(DirectoryListerTest, CancelOnListDoneTest) {
252   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
253   DirectoryLister lister(root_path(), &delegate);
254   delegate.set_cancel_lister_on_list_done(true);
255   delegate.Run(&lister);
256 
257   EXPECT_TRUE(delegate.done());
258   EXPECT_THAT(delegate.error(), IsOk());
259   EXPECT_EQ(expected_list_length_non_recursive(), delegate.num_files());
260 }
261 
TEST_F(DirectoryListerTest,CancelOnLastElementTest)262 TEST_F(DirectoryListerTest, CancelOnLastElementTest) {
263   base::ScopedTempDir tempDir;
264   EXPECT_TRUE(tempDir.CreateUniqueTempDir());
265 
266   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
267   DirectoryLister lister(tempDir.GetPath(), &delegate);
268   delegate.set_cancel_lister_on_list_file(true);
269   delegate.Run(&lister);
270 
271   EXPECT_FALSE(delegate.done());
272   // Contains only the parent directory ("..").
273   EXPECT_EQ(1, delegate.num_files());
274 }
275 
TEST_F(DirectoryListerTest,NoSuchDirTest)276 TEST_F(DirectoryListerTest, NoSuchDirTest) {
277   base::ScopedTempDir tempDir;
278   EXPECT_TRUE(tempDir.CreateUniqueTempDir());
279 
280   ListerDelegate delegate(DirectoryLister::ALPHA_DIRS_FIRST);
281   DirectoryLister lister(
282       tempDir.GetPath().AppendASCII("this_path_does_not_exist"), &delegate);
283   delegate.Run(&lister);
284 
285   EXPECT_THAT(delegate.error(), IsError(ERR_FILE_NOT_FOUND));
286   EXPECT_EQ(0, delegate.num_files());
287 }
288 
289 }  // namespace net
290