file_path_watcher_unittest.cc 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/files/file_path_watcher.h"
  5. #include <memory>
  6. #include <set>
  7. #include <string>
  8. #include <vector>
  9. #include "base/bind.h"
  10. #include "base/callback_helpers.h"
  11. #include "base/files/file_path.h"
  12. #include "base/files/file_util.h"
  13. #include "base/files/scoped_temp_dir.h"
  14. #include "base/location.h"
  15. #include "base/logging.h"
  16. #include "base/run_loop.h"
  17. #include "base/strings/stringprintf.h"
  18. #include "base/synchronization/waitable_event.h"
  19. #include "base/task/single_thread_task_runner.h"
  20. #include "base/test/bind.h"
  21. #include "base/test/task_environment.h"
  22. #include "base/test/test_file_util.h"
  23. #include "base/test/test_timeouts.h"
  24. #include "base/threading/thread.h"
  25. #include "base/threading/thread_task_runner_handle.h"
  26. #include "build/build_config.h"
  27. #include "testing/gtest/include/gtest/gtest.h"
  28. #if BUILDFLAG(IS_WIN)
  29. #include <windows.h>
  30. #include <aclapi.h>
  31. #elif BUILDFLAG(IS_POSIX)
  32. #include <sys/stat.h>
  33. #endif
  34. #if BUILDFLAG(IS_ANDROID)
  35. #include "base/android/path_utils.h"
  36. #endif // BUILDFLAG(IS_ANDROID)
  37. #if BUILDFLAG(IS_POSIX)
  38. #include "base/files/file_descriptor_watcher_posix.h"
  39. #endif // BUILDFLAG(IS_POSIX)
  40. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
  41. #include "base/files/file_path_watcher_inotify.h"
  42. #include "base/format_macros.h"
  43. #endif
  44. namespace base {
  45. namespace {
  46. class TestDelegate;
  47. // Aggregates notifications from the test delegates and breaks the run loop
  48. // the test thread is waiting on once they all came in.
  49. class NotificationCollector
  50. : public base::RefCountedThreadSafe<NotificationCollector> {
  51. public:
  52. NotificationCollector() : task_runner_(ThreadTaskRunnerHandle::Get()) {}
  53. // Called from the file thread by the delegates.
  54. void OnChange(TestDelegate* delegate) {
  55. task_runner_->PostTask(
  56. FROM_HERE, base::BindOnce(&NotificationCollector::RecordChange, this,
  57. base::Unretained(delegate)));
  58. }
  59. void Register(TestDelegate* delegate) {
  60. delegates_.insert(delegate);
  61. }
  62. void Reset(base::OnceClosure signal_closure) {
  63. signal_closure_ = std::move(signal_closure);
  64. signaled_.clear();
  65. }
  66. bool Success() {
  67. return signaled_ == delegates_;
  68. }
  69. private:
  70. friend class base::RefCountedThreadSafe<NotificationCollector>;
  71. ~NotificationCollector() = default;
  72. void RecordChange(TestDelegate* delegate) {
  73. // Warning: |delegate| is Unretained. Do not dereference.
  74. ASSERT_TRUE(task_runner_->BelongsToCurrentThread());
  75. ASSERT_TRUE(delegates_.count(delegate));
  76. signaled_.insert(delegate);
  77. // Check whether all delegates have been signaled.
  78. if (signal_closure_ && signaled_ == delegates_)
  79. std::move(signal_closure_).Run();
  80. }
  81. // Set of registered delegates.
  82. std::set<TestDelegate*> delegates_;
  83. // Set of signaled delegates.
  84. std::set<TestDelegate*> signaled_;
  85. // The loop we should break after all delegates signaled.
  86. scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
  87. // Closure to run when all delegates have signaled.
  88. base::OnceClosure signal_closure_;
  89. };
  90. class TestDelegateBase : public SupportsWeakPtr<TestDelegateBase> {
  91. public:
  92. TestDelegateBase() = default;
  93. TestDelegateBase(const TestDelegateBase&) = delete;
  94. TestDelegateBase& operator=(const TestDelegateBase&) = delete;
  95. virtual ~TestDelegateBase() = default;
  96. virtual void OnFileChanged(const FilePath& path, bool error) = 0;
  97. };
  98. // A mock class for testing. Gmock is not appropriate because it is not
  99. // thread-safe for setting expectations. Thus the test code cannot safely
  100. // reset expectations while the file watcher is running.
  101. // Instead, TestDelegate gets the notifications from FilePathWatcher and uses
  102. // NotificationCollector to aggregate the results.
  103. class TestDelegate : public TestDelegateBase {
  104. public:
  105. explicit TestDelegate(NotificationCollector* collector)
  106. : collector_(collector) {
  107. collector_->Register(this);
  108. }
  109. TestDelegate(const TestDelegate&) = delete;
  110. TestDelegate& operator=(const TestDelegate&) = delete;
  111. ~TestDelegate() override = default;
  112. // Configure this delegate so that it expects an error.
  113. void set_expect_error() { expect_error_ = true; }
  114. // TestDelegateBase:
  115. void OnFileChanged(const FilePath& path, bool error) override {
  116. if (error != expect_error_) {
  117. ADD_FAILURE() << "Unexpected change for \"" << path
  118. << "\" with |error| = " << (error ? "true" : "false");
  119. } else {
  120. collector_->OnChange(this);
  121. }
  122. }
  123. private:
  124. scoped_refptr<NotificationCollector> collector_;
  125. bool expect_error_ = false;
  126. };
  127. class FilePathWatcherTest : public testing::Test {
  128. public:
  129. FilePathWatcherTest()
  130. #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
  131. : task_environment_(test::TaskEnvironment::MainThreadType::IO)
  132. #endif
  133. {
  134. }
  135. FilePathWatcherTest(const FilePathWatcherTest&) = delete;
  136. FilePathWatcherTest& operator=(const FilePathWatcherTest&) = delete;
  137. ~FilePathWatcherTest() override = default;
  138. protected:
  139. void SetUp() override {
  140. #if BUILDFLAG(IS_ANDROID)
  141. // Watching files is only permitted when all parent directories are
  142. // accessible, which is not the case for the default temp directory
  143. // on Android which is under /data/data. Use /sdcard instead.
  144. // TODO(pauljensen): Remove this when crbug.com/475568 is fixed.
  145. FilePath parent_dir;
  146. ASSERT_TRUE(android::GetExternalStorageDirectory(&parent_dir));
  147. ASSERT_TRUE(temp_dir_.CreateUniqueTempDirUnderPath(parent_dir));
  148. #else // BUILDFLAG(IS_ANDROID)
  149. ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  150. #endif // BUILDFLAG(IS_ANDROID)
  151. collector_ = new NotificationCollector();
  152. }
  153. void TearDown() override { RunLoop().RunUntilIdle(); }
  154. FilePath test_file() {
  155. return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest");
  156. }
  157. FilePath test_link() {
  158. return temp_dir_.GetPath().AppendASCII("FilePathWatcherTest.lnk");
  159. }
  160. [[nodiscard]] bool SetupWatch(const FilePath& target,
  161. FilePathWatcher* watcher,
  162. TestDelegateBase* delegate,
  163. FilePathWatcher::Type watch_type);
  164. [[nodiscard]] bool WaitForEvents() {
  165. return WaitForEventsWithTimeout(TestTimeouts::action_timeout());
  166. }
  167. [[nodiscard]] bool WaitForEventsWithTimeout(TimeDelta timeout) {
  168. RunLoop run_loop;
  169. collector_->Reset(run_loop.QuitClosure());
  170. // Make sure we timeout if we don't get notified.
  171. ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  172. FROM_HERE, run_loop.QuitClosure(), timeout);
  173. run_loop.Run();
  174. return collector_->Success();
  175. }
  176. NotificationCollector* collector() { return collector_.get(); }
  177. test::TaskEnvironment task_environment_;
  178. ScopedTempDir temp_dir_;
  179. scoped_refptr<NotificationCollector> collector_;
  180. };
  181. bool FilePathWatcherTest::SetupWatch(const FilePath& target,
  182. FilePathWatcher* watcher,
  183. TestDelegateBase* delegate,
  184. FilePathWatcher::Type watch_type) {
  185. return watcher->Watch(target, watch_type,
  186. base::BindRepeating(&TestDelegateBase::OnFileChanged,
  187. delegate->AsWeakPtr()));
  188. }
  189. // Basic test: Create the file and verify that we notice.
  190. TEST_F(FilePathWatcherTest, NewFile) {
  191. FilePathWatcher watcher;
  192. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  193. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  194. FilePathWatcher::Type::kNonRecursive));
  195. ASSERT_TRUE(WriteFile(test_file(), "content"));
  196. ASSERT_TRUE(WaitForEvents());
  197. }
  198. // Verify that modifying the file is caught.
  199. TEST_F(FilePathWatcherTest, ModifiedFile) {
  200. ASSERT_TRUE(WriteFile(test_file(), "content"));
  201. FilePathWatcher watcher;
  202. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  203. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  204. FilePathWatcher::Type::kNonRecursive));
  205. // Now make sure we get notified if the file is modified.
  206. ASSERT_TRUE(WriteFile(test_file(), "new content"));
  207. ASSERT_TRUE(WaitForEvents());
  208. }
  209. // Verify that moving the file into place is caught.
  210. #if BUILDFLAG(IS_FUCHSIA)
  211. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  212. #define MAYBE_MovedFile DISABLED_MovedFile
  213. #else
  214. #define MAYBE_MovedFile MovedFile
  215. #endif
  216. TEST_F(FilePathWatcherTest, MAYBE_MovedFile) {
  217. FilePath source_file(temp_dir_.GetPath().AppendASCII("source"));
  218. ASSERT_TRUE(WriteFile(source_file, "content"));
  219. FilePathWatcher watcher;
  220. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  221. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  222. FilePathWatcher::Type::kNonRecursive));
  223. // Now make sure we get notified if the file is modified.
  224. ASSERT_TRUE(base::Move(source_file, test_file()));
  225. ASSERT_TRUE(WaitForEvents());
  226. }
  227. #if BUILDFLAG(IS_FUCHSIA)
  228. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  229. #define MAYBE_DeletedFile DISABLED_DeletedFile
  230. #else
  231. #define MAYBE_DeletedFile DeletedFile
  232. #endif
  233. TEST_F(FilePathWatcherTest, MAYBE_DeletedFile) {
  234. ASSERT_TRUE(WriteFile(test_file(), "content"));
  235. FilePathWatcher watcher;
  236. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  237. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  238. FilePathWatcher::Type::kNonRecursive));
  239. // Now make sure we get notified if the file is deleted.
  240. base::DeleteFile(test_file());
  241. ASSERT_TRUE(WaitForEvents());
  242. }
  243. // Used by the DeleteDuringNotify test below.
  244. // Deletes the FilePathWatcher when it's notified.
  245. class Deleter : public TestDelegateBase {
  246. public:
  247. explicit Deleter(base::OnceClosure done_closure)
  248. : watcher_(std::make_unique<FilePathWatcher>()),
  249. done_closure_(std::move(done_closure)) {}
  250. Deleter(const Deleter&) = delete;
  251. Deleter& operator=(const Deleter&) = delete;
  252. ~Deleter() override = default;
  253. void OnFileChanged(const FilePath&, bool) override {
  254. watcher_.reset();
  255. std::move(done_closure_).Run();
  256. }
  257. FilePathWatcher* watcher() const { return watcher_.get(); }
  258. private:
  259. std::unique_ptr<FilePathWatcher> watcher_;
  260. base::OnceClosure done_closure_;
  261. };
  262. // Verify that deleting a watcher during the callback doesn't crash.
  263. TEST_F(FilePathWatcherTest, DeleteDuringNotify) {
  264. base::RunLoop run_loop;
  265. Deleter deleter(run_loop.QuitClosure());
  266. ASSERT_TRUE(SetupWatch(test_file(), deleter.watcher(), &deleter,
  267. FilePathWatcher::Type::kNonRecursive));
  268. ASSERT_TRUE(WriteFile(test_file(), "content"));
  269. run_loop.Run();
  270. // We win if we haven't crashed yet.
  271. // Might as well double-check it got deleted, too.
  272. ASSERT_TRUE(deleter.watcher() == nullptr);
  273. }
  274. // Verify that deleting the watcher works even if there is a pending
  275. // notification.
  276. TEST_F(FilePathWatcherTest, DestroyWithPendingNotification) {
  277. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  278. FilePathWatcher watcher;
  279. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  280. FilePathWatcher::Type::kNonRecursive));
  281. ASSERT_TRUE(WriteFile(test_file(), "content"));
  282. }
  283. #if BUILDFLAG(IS_FUCHSIA)
  284. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  285. #define MAYBE_MultipleWatchersSingleFile DISABLED_MultipleWatchersSingleFile
  286. #else
  287. #define MAYBE_MultipleWatchersSingleFile MultipleWatchersSingleFile
  288. #endif
  289. TEST_F(FilePathWatcherTest, MAYBE_MultipleWatchersSingleFile) {
  290. FilePathWatcher watcher1, watcher2;
  291. std::unique_ptr<TestDelegate> delegate1(new TestDelegate(collector()));
  292. std::unique_ptr<TestDelegate> delegate2(new TestDelegate(collector()));
  293. ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get(),
  294. FilePathWatcher::Type::kNonRecursive));
  295. ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get(),
  296. FilePathWatcher::Type::kNonRecursive));
  297. ASSERT_TRUE(WriteFile(test_file(), "content"));
  298. ASSERT_TRUE(WaitForEvents());
  299. }
  300. // Verify that watching a file whose parent directory doesn't exist yet works if
  301. // the directory and file are created eventually.
  302. #if BUILDFLAG(IS_FUCHSIA)
  303. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  304. #define MAYBE_NonExistentDirectory DISABLED_NonExistentDirectory
  305. #else
  306. #define MAYBE_NonExistentDirectory NonExistentDirectory
  307. #endif
  308. TEST_F(FilePathWatcherTest, MAYBE_NonExistentDirectory) {
  309. FilePathWatcher watcher;
  310. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  311. FilePath file(dir.AppendASCII("file"));
  312. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  313. ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(),
  314. FilePathWatcher::Type::kNonRecursive));
  315. ASSERT_TRUE(base::CreateDirectory(dir));
  316. ASSERT_TRUE(WriteFile(file, "content"));
  317. VLOG(1) << "Waiting for file creation";
  318. ASSERT_TRUE(WaitForEvents());
  319. ASSERT_TRUE(WriteFile(file, "content v2"));
  320. VLOG(1) << "Waiting for file change";
  321. ASSERT_TRUE(WaitForEvents());
  322. ASSERT_TRUE(base::DeleteFile(file));
  323. VLOG(1) << "Waiting for file deletion";
  324. ASSERT_TRUE(WaitForEvents());
  325. }
  326. // Exercises watch reconfiguration for the case that directories on the path
  327. // are rapidly created.
  328. #if BUILDFLAG(IS_FUCHSIA)
  329. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  330. #define MAYBE_DirectoryChain DISABLED_DirectoryChain
  331. #else
  332. #define MAYBE_DirectoryChain DirectoryChain
  333. #endif
  334. TEST_F(FilePathWatcherTest, MAYBE_DirectoryChain) {
  335. FilePath path(temp_dir_.GetPath());
  336. std::vector<std::string> dir_names;
  337. for (int i = 0; i < 20; i++) {
  338. std::string dir(base::StringPrintf("d%d", i));
  339. dir_names.push_back(dir);
  340. path = path.AppendASCII(dir);
  341. }
  342. FilePathWatcher watcher;
  343. FilePath file(path.AppendASCII("file"));
  344. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  345. ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(),
  346. FilePathWatcher::Type::kNonRecursive));
  347. FilePath sub_path(temp_dir_.GetPath());
  348. for (std::vector<std::string>::const_iterator d(dir_names.begin());
  349. d != dir_names.end(); ++d) {
  350. sub_path = sub_path.AppendASCII(*d);
  351. ASSERT_TRUE(base::CreateDirectory(sub_path));
  352. }
  353. VLOG(1) << "Create File";
  354. ASSERT_TRUE(WriteFile(file, "content"));
  355. VLOG(1) << "Waiting for file creation";
  356. ASSERT_TRUE(WaitForEvents());
  357. ASSERT_TRUE(WriteFile(file, "content v2"));
  358. VLOG(1) << "Waiting for file modification";
  359. ASSERT_TRUE(WaitForEvents());
  360. }
  361. #if BUILDFLAG(IS_FUCHSIA)
  362. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  363. #define MAYBE_DisappearingDirectory DISABLED_DisappearingDirectory
  364. #else
  365. #define MAYBE_DisappearingDirectory DisappearingDirectory
  366. #endif
  367. TEST_F(FilePathWatcherTest, MAYBE_DisappearingDirectory) {
  368. FilePathWatcher watcher;
  369. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  370. FilePath file(dir.AppendASCII("file"));
  371. ASSERT_TRUE(base::CreateDirectory(dir));
  372. ASSERT_TRUE(WriteFile(file, "content"));
  373. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  374. ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(),
  375. FilePathWatcher::Type::kNonRecursive));
  376. ASSERT_TRUE(base::DeletePathRecursively(dir));
  377. ASSERT_TRUE(WaitForEvents());
  378. }
  379. // Tests that a file that is deleted and reappears is tracked correctly.
  380. #if BUILDFLAG(IS_FUCHSIA)
  381. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  382. #define MAYBE_DeleteAndRecreate DISABLED_DeleteAndRecreate
  383. #else
  384. #define MAYBE_DeleteAndRecreate DeleteAndRecreate
  385. #endif
  386. TEST_F(FilePathWatcherTest, MAYBE_DeleteAndRecreate) {
  387. ASSERT_TRUE(WriteFile(test_file(), "content"));
  388. FilePathWatcher watcher;
  389. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  390. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  391. FilePathWatcher::Type::kNonRecursive));
  392. ASSERT_TRUE(base::DeleteFile(test_file()));
  393. VLOG(1) << "Waiting for file deletion";
  394. ASSERT_TRUE(WaitForEvents());
  395. ASSERT_TRUE(WriteFile(test_file(), "content"));
  396. VLOG(1) << "Waiting for file creation";
  397. ASSERT_TRUE(WaitForEvents());
  398. }
  399. #if BUILDFLAG(IS_FUCHSIA)
  400. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  401. #define MAYBE_WatchDirectory DISABLED_WatchDirectory
  402. #else
  403. #define MAYBE_WatchDirectory WatchDirectory
  404. #endif
  405. TEST_F(FilePathWatcherTest, MAYBE_WatchDirectory) {
  406. FilePathWatcher watcher;
  407. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  408. FilePath file1(dir.AppendASCII("file1"));
  409. FilePath file2(dir.AppendASCII("file2"));
  410. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  411. ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(),
  412. FilePathWatcher::Type::kNonRecursive));
  413. ASSERT_TRUE(base::CreateDirectory(dir));
  414. VLOG(1) << "Waiting for directory creation";
  415. ASSERT_TRUE(WaitForEvents());
  416. ASSERT_TRUE(WriteFile(file1, "content"));
  417. VLOG(1) << "Waiting for file1 creation";
  418. ASSERT_TRUE(WaitForEvents());
  419. #if !BUILDFLAG(IS_APPLE)
  420. // Mac implementation does not detect files modified in a directory.
  421. ASSERT_TRUE(WriteFile(file1, "content v2"));
  422. VLOG(1) << "Waiting for file1 modification";
  423. ASSERT_TRUE(WaitForEvents());
  424. #endif // !BUILDFLAG(IS_APPLE)
  425. ASSERT_TRUE(base::DeleteFile(file1));
  426. VLOG(1) << "Waiting for file1 deletion";
  427. ASSERT_TRUE(WaitForEvents());
  428. ASSERT_TRUE(WriteFile(file2, "content"));
  429. VLOG(1) << "Waiting for file2 creation";
  430. ASSERT_TRUE(WaitForEvents());
  431. }
  432. #if BUILDFLAG(IS_FUCHSIA)
  433. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  434. #define MAYBE_MoveParent DISABLED_MoveParent
  435. #else
  436. #define MAYBE_MoveParent MoveParent
  437. #endif
  438. TEST_F(FilePathWatcherTest, MAYBE_MoveParent) {
  439. FilePathWatcher file_watcher;
  440. FilePathWatcher subdir_watcher;
  441. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  442. FilePath dest(temp_dir_.GetPath().AppendASCII("dest"));
  443. FilePath subdir(dir.AppendASCII("subdir"));
  444. FilePath file(subdir.AppendASCII("file"));
  445. std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
  446. ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get(),
  447. FilePathWatcher::Type::kNonRecursive));
  448. std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
  449. ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get(),
  450. FilePathWatcher::Type::kNonRecursive));
  451. // Setup a directory hierarchy.
  452. ASSERT_TRUE(base::CreateDirectory(subdir));
  453. ASSERT_TRUE(WriteFile(file, "content"));
  454. VLOG(1) << "Waiting for file creation";
  455. ASSERT_TRUE(WaitForEvents());
  456. // Move the parent directory.
  457. base::Move(dir, dest);
  458. VLOG(1) << "Waiting for directory move";
  459. ASSERT_TRUE(WaitForEvents());
  460. }
  461. #if BUILDFLAG(IS_FUCHSIA)
  462. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  463. #define MAYBE_RecursiveWatch DISABLED_RecursiveWatch
  464. #else
  465. #define MAYBE_RecursiveWatch RecursiveWatch
  466. #endif
  467. TEST_F(FilePathWatcherTest, MAYBE_RecursiveWatch) {
  468. FilePathWatcher watcher;
  469. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  470. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  471. bool setup_result = SetupWatch(dir, &watcher, delegate.get(),
  472. FilePathWatcher::Type::kRecursive);
  473. if (!FilePathWatcher::RecursiveWatchAvailable()) {
  474. ASSERT_FALSE(setup_result);
  475. return;
  476. }
  477. ASSERT_TRUE(setup_result);
  478. // Main directory("dir") creation.
  479. ASSERT_TRUE(base::CreateDirectory(dir));
  480. ASSERT_TRUE(WaitForEvents());
  481. // Create "$dir/file1".
  482. FilePath file1(dir.AppendASCII("file1"));
  483. ASSERT_TRUE(WriteFile(file1, "content"));
  484. ASSERT_TRUE(WaitForEvents());
  485. // Create "$dir/subdir".
  486. FilePath subdir(dir.AppendASCII("subdir"));
  487. ASSERT_TRUE(base::CreateDirectory(subdir));
  488. ASSERT_TRUE(WaitForEvents());
  489. // Create "$dir/subdir/subdir2".
  490. FilePath subdir2(subdir.AppendASCII("subdir2"));
  491. ASSERT_TRUE(base::CreateDirectory(subdir2));
  492. ASSERT_TRUE(WaitForEvents());
  493. // Rename "$dir/subdir/subdir2" to "$dir/subdir/subdir2b".
  494. FilePath subdir2b(subdir.AppendASCII("subdir2b"));
  495. base::Move(subdir2, subdir2b);
  496. ASSERT_TRUE(WaitForEvents());
  497. // Mac and Win don't generate events for Touch.
  498. // Android TouchFile returns false.
  499. #if !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
  500. // Touch "$dir".
  501. Time access_time;
  502. ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00", &access_time));
  503. ASSERT_TRUE(base::TouchFile(dir, access_time, access_time));
  504. ASSERT_TRUE(WaitForEvents());
  505. #endif // !(BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_ANDROID))
  506. // Create "$dir/subdir/subdir_file1".
  507. FilePath subdir_file1(subdir.AppendASCII("subdir_file1"));
  508. ASSERT_TRUE(WriteFile(subdir_file1, "content"));
  509. ASSERT_TRUE(WaitForEvents());
  510. // Create "$dir/subdir/subdir_child_dir".
  511. FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
  512. ASSERT_TRUE(base::CreateDirectory(subdir_child_dir));
  513. ASSERT_TRUE(WaitForEvents());
  514. // Create "$dir/subdir/subdir_child_dir/child_dir_file1".
  515. FilePath child_dir_file1(subdir_child_dir.AppendASCII("child_dir_file1"));
  516. ASSERT_TRUE(WriteFile(child_dir_file1, "content v2"));
  517. ASSERT_TRUE(WaitForEvents());
  518. // Write into "$dir/subdir/subdir_child_dir/child_dir_file1".
  519. ASSERT_TRUE(WriteFile(child_dir_file1, "content"));
  520. ASSERT_TRUE(WaitForEvents());
  521. // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
  522. // "fuse" file system, while /data uses "ext4". Running these tests in /data
  523. // would be preferable and allow testing file attributes and symlinks.
  524. // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
  525. // the |temp_dir_| in /data.
  526. #if !BUILDFLAG(IS_ANDROID)
  527. // Modify "$dir/subdir/subdir_child_dir/child_dir_file1" attributes.
  528. ASSERT_TRUE(base::MakeFileUnreadable(child_dir_file1));
  529. ASSERT_TRUE(WaitForEvents());
  530. #endif
  531. // Delete "$dir/subdir/subdir_file1".
  532. ASSERT_TRUE(base::DeleteFile(subdir_file1));
  533. ASSERT_TRUE(WaitForEvents());
  534. // Delete "$dir/subdir/subdir_child_dir/child_dir_file1".
  535. ASSERT_TRUE(base::DeleteFile(child_dir_file1));
  536. ASSERT_TRUE(WaitForEvents());
  537. }
  538. #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
  539. // Apps cannot create symlinks on Android in /sdcard as /sdcard uses the
  540. // "fuse" file system, while /data uses "ext4". Running these tests in /data
  541. // would be preferable and allow testing file attributes and symlinks.
  542. // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
  543. // the |temp_dir_| in /data.
  544. //
  545. // This test is disabled on Fuchsia since it doesn't support symlinking.
  546. TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
  547. if (!FilePathWatcher::RecursiveWatchAvailable())
  548. return;
  549. FilePathWatcher watcher;
  550. FilePath test_dir(temp_dir_.GetPath().AppendASCII("test_dir"));
  551. ASSERT_TRUE(base::CreateDirectory(test_dir));
  552. FilePath symlink(test_dir.AppendASCII("symlink"));
  553. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  554. ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(),
  555. FilePathWatcher::Type::kRecursive));
  556. // Link creation.
  557. FilePath target1(temp_dir_.GetPath().AppendASCII("target1"));
  558. ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
  559. ASSERT_TRUE(WaitForEvents());
  560. // Target1 creation.
  561. ASSERT_TRUE(base::CreateDirectory(target1));
  562. ASSERT_TRUE(WaitForEvents());
  563. // Create a file in target1.
  564. FilePath target1_file(target1.AppendASCII("file"));
  565. ASSERT_TRUE(WriteFile(target1_file, "content"));
  566. ASSERT_TRUE(WaitForEvents());
  567. // Link change.
  568. FilePath target2(temp_dir_.GetPath().AppendASCII("target2"));
  569. ASSERT_TRUE(base::CreateDirectory(target2));
  570. ASSERT_TRUE(base::DeleteFile(symlink));
  571. ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink));
  572. ASSERT_TRUE(WaitForEvents());
  573. // Create a file in target2.
  574. FilePath target2_file(target2.AppendASCII("file"));
  575. ASSERT_TRUE(WriteFile(target2_file, "content"));
  576. ASSERT_TRUE(WaitForEvents());
  577. }
  578. #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
  579. #if BUILDFLAG(IS_FUCHSIA)
  580. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  581. #define MAYBE_MoveChild DISABLED_MoveChild
  582. #else
  583. #define MAYBE_MoveChild MoveChild
  584. #endif
  585. TEST_F(FilePathWatcherTest, MAYBE_MoveChild) {
  586. FilePathWatcher file_watcher;
  587. FilePathWatcher subdir_watcher;
  588. FilePath source_dir(temp_dir_.GetPath().AppendASCII("source"));
  589. FilePath source_subdir(source_dir.AppendASCII("subdir"));
  590. FilePath source_file(source_subdir.AppendASCII("file"));
  591. FilePath dest_dir(temp_dir_.GetPath().AppendASCII("dest"));
  592. FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
  593. FilePath dest_file(dest_subdir.AppendASCII("file"));
  594. // Setup a directory hierarchy.
  595. ASSERT_TRUE(base::CreateDirectory(source_subdir));
  596. ASSERT_TRUE(WriteFile(source_file, "content"));
  597. std::unique_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
  598. ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get(),
  599. FilePathWatcher::Type::kNonRecursive));
  600. std::unique_ptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
  601. ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get(),
  602. FilePathWatcher::Type::kNonRecursive));
  603. // Move the directory into place, s.t. the watched file appears.
  604. ASSERT_TRUE(base::Move(source_dir, dest_dir));
  605. ASSERT_TRUE(WaitForEvents());
  606. }
  607. // Verify that changing attributes on a file is caught
  608. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
  609. // TODO(crbug.com/851641): Re-enable for Fuchsia when inotify is fixed.
  610. // Apps cannot change file attributes on Android in /sdcard as /sdcard uses the
  611. // "fuse" file system, while /data uses "ext4". Running these tests in /data
  612. // would be preferable and allow testing file attributes and symlinks.
  613. // TODO(pauljensen): Re-enable when crbug.com/475568 is fixed and SetUp() places
  614. // the |temp_dir_| in /data.
  615. #define MAYBE_FileAttributesChanged DISABLED_FileAttributesChanged
  616. #else
  617. #define MAYBE_FileAttributesChanged FileAttributesChanged
  618. #endif // BUILDFLAG(IS_ANDROID)
  619. TEST_F(FilePathWatcherTest, MAYBE_FileAttributesChanged) {
  620. ASSERT_TRUE(WriteFile(test_file(), "content"));
  621. FilePathWatcher watcher;
  622. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  623. ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get(),
  624. FilePathWatcher::Type::kNonRecursive));
  625. // Now make sure we get notified if the file is modified.
  626. ASSERT_TRUE(base::MakeFileUnreadable(test_file()));
  627. ASSERT_TRUE(WaitForEvents());
  628. }
  629. #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  630. // Verify that creating a symlink is caught.
  631. TEST_F(FilePathWatcherTest, CreateLink) {
  632. FilePathWatcher watcher;
  633. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  634. // Note that we are watching the symlink
  635. ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
  636. FilePathWatcher::Type::kNonRecursive));
  637. // Now make sure we get notified if the link is created.
  638. // Note that test_file() doesn't have to exist.
  639. ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
  640. ASSERT_TRUE(WaitForEvents());
  641. }
  642. // Verify that deleting a symlink is caught.
  643. TEST_F(FilePathWatcherTest, DeleteLink) {
  644. // Unfortunately this test case only works if the link target exists.
  645. // TODO(craig) fix this as part of crbug.com/91561.
  646. ASSERT_TRUE(WriteFile(test_file(), "content"));
  647. ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
  648. FilePathWatcher watcher;
  649. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  650. ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
  651. FilePathWatcher::Type::kNonRecursive));
  652. // Now make sure we get notified if the link is deleted.
  653. ASSERT_TRUE(base::DeleteFile(test_link()));
  654. ASSERT_TRUE(WaitForEvents());
  655. }
  656. // Verify that modifying a target file that a link is pointing to
  657. // when we are watching the link is caught.
  658. TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
  659. ASSERT_TRUE(WriteFile(test_file(), "content"));
  660. ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
  661. FilePathWatcher watcher;
  662. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  663. // Note that we are watching the symlink.
  664. ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
  665. FilePathWatcher::Type::kNonRecursive));
  666. // Now make sure we get notified if the file is modified.
  667. ASSERT_TRUE(WriteFile(test_file(), "new content"));
  668. ASSERT_TRUE(WaitForEvents());
  669. }
  670. // Verify that creating a target file that a link is pointing to
  671. // when we are watching the link is caught.
  672. TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
  673. ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
  674. FilePathWatcher watcher;
  675. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  676. // Note that we are watching the symlink.
  677. ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
  678. FilePathWatcher::Type::kNonRecursive));
  679. // Now make sure we get notified if the target file is created.
  680. ASSERT_TRUE(WriteFile(test_file(), "content"));
  681. ASSERT_TRUE(WaitForEvents());
  682. }
  683. // Verify that deleting a target file that a link is pointing to
  684. // when we are watching the link is caught.
  685. TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
  686. ASSERT_TRUE(WriteFile(test_file(), "content"));
  687. ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
  688. FilePathWatcher watcher;
  689. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  690. // Note that we are watching the symlink.
  691. ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(),
  692. FilePathWatcher::Type::kNonRecursive));
  693. // Now make sure we get notified if the target file is deleted.
  694. ASSERT_TRUE(base::DeleteFile(test_file()));
  695. ASSERT_TRUE(WaitForEvents());
  696. }
  697. // Verify that watching a file whose parent directory is a link that
  698. // doesn't exist yet works if the symlink is created eventually.
  699. TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
  700. FilePathWatcher watcher;
  701. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  702. FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
  703. FilePath file(dir.AppendASCII("file"));
  704. FilePath linkfile(link_dir.AppendASCII("file"));
  705. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  706. // dir/file should exist.
  707. ASSERT_TRUE(base::CreateDirectory(dir));
  708. ASSERT_TRUE(WriteFile(file, "content"));
  709. // Note that we are watching dir.lnk/file which doesn't exist yet.
  710. ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(),
  711. FilePathWatcher::Type::kNonRecursive));
  712. ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
  713. VLOG(1) << "Waiting for link creation";
  714. ASSERT_TRUE(WaitForEvents());
  715. ASSERT_TRUE(WriteFile(file, "content v2"));
  716. VLOG(1) << "Waiting for file change";
  717. ASSERT_TRUE(WaitForEvents());
  718. ASSERT_TRUE(base::DeleteFile(file));
  719. VLOG(1) << "Waiting for file deletion";
  720. ASSERT_TRUE(WaitForEvents());
  721. }
  722. // Verify that watching a file whose parent directory is a
  723. // dangling symlink works if the directory is created eventually.
  724. TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
  725. FilePathWatcher watcher;
  726. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  727. FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
  728. FilePath file(dir.AppendASCII("file"));
  729. FilePath linkfile(link_dir.AppendASCII("file"));
  730. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  731. // Now create the link from dir.lnk pointing to dir but
  732. // neither dir nor dir/file exist yet.
  733. ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
  734. // Note that we are watching dir.lnk/file.
  735. ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(),
  736. FilePathWatcher::Type::kNonRecursive));
  737. ASSERT_TRUE(base::CreateDirectory(dir));
  738. ASSERT_TRUE(WriteFile(file, "content"));
  739. VLOG(1) << "Waiting for dir/file creation";
  740. ASSERT_TRUE(WaitForEvents());
  741. ASSERT_TRUE(WriteFile(file, "content v2"));
  742. VLOG(1) << "Waiting for file change";
  743. ASSERT_TRUE(WaitForEvents());
  744. ASSERT_TRUE(base::DeleteFile(file));
  745. VLOG(1) << "Waiting for file deletion";
  746. ASSERT_TRUE(WaitForEvents());
  747. }
  748. // Verify that watching a file with a symlink on the path
  749. // to the file works.
  750. TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
  751. FilePathWatcher watcher;
  752. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  753. FilePath link_dir(temp_dir_.GetPath().AppendASCII("dir.lnk"));
  754. FilePath file(dir.AppendASCII("file"));
  755. FilePath linkfile(link_dir.AppendASCII("file"));
  756. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  757. ASSERT_TRUE(base::CreateDirectory(dir));
  758. ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
  759. // Note that we are watching dir.lnk/file but the file doesn't exist yet.
  760. ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(),
  761. FilePathWatcher::Type::kNonRecursive));
  762. ASSERT_TRUE(WriteFile(file, "content"));
  763. VLOG(1) << "Waiting for file creation";
  764. ASSERT_TRUE(WaitForEvents());
  765. ASSERT_TRUE(WriteFile(file, "content v2"));
  766. VLOG(1) << "Waiting for file change";
  767. ASSERT_TRUE(WaitForEvents());
  768. ASSERT_TRUE(base::DeleteFile(file));
  769. VLOG(1) << "Waiting for file deletion";
  770. ASSERT_TRUE(WaitForEvents());
  771. }
  772. // Regression tests that FilePathWatcherImpl does not leave its reference in
  773. // `g_inotify_reader` due to a race in recursive watch.
  774. // See https://crbug.com/990004.
  775. TEST_F(FilePathWatcherTest, RacyRecursiveWatch) {
  776. if (!FilePathWatcher::RecursiveWatchAvailable())
  777. GTEST_SKIP();
  778. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  779. // Create and delete many subdirs. 20 is an arbitrary number big enough
  780. // to have more chances to make FilePathWatcherImpl leak watchers.
  781. std::vector<FilePath> subdirs;
  782. for (int i = 0; i < 20; ++i)
  783. subdirs.emplace_back(dir.AppendASCII(base::StringPrintf("subdir_%d", i)));
  784. Thread subdir_updater("SubDir Updater");
  785. ASSERT_TRUE(subdir_updater.Start());
  786. auto subdir_update_task = base::BindLambdaForTesting([&]() {
  787. for (const auto& subdir : subdirs) {
  788. // First update event to trigger watch callback.
  789. ASSERT_TRUE(CreateDirectory(subdir));
  790. // Second update event. The notification sent for this event will race
  791. // with the upcoming deletion of the directory below. This test is about
  792. // verifying that the impl handles this.
  793. FilePath subdir_file(subdir.AppendASCII("subdir_file"));
  794. ASSERT_TRUE(WriteFile(subdir_file, "content"));
  795. // Racy subdir delete to trigger watcher leak.
  796. ASSERT_TRUE(DeletePathRecursively(subdir));
  797. }
  798. });
  799. // Try the racy subdir update 100 times.
  800. for (int i = 0; i < 100; ++i) {
  801. RunLoop run_loop;
  802. auto watcher = std::make_unique<FilePathWatcher>();
  803. // Keep watch callback in `watcher_callback` so that "watcher.reset()"
  804. // inside does not release the callback and the lambda capture with it.
  805. // Otherwise, accessing `run_loop` as part of the lamda capture would be
  806. // use-after-free under asan.
  807. auto watcher_callback =
  808. base::BindLambdaForTesting([&](const FilePath& path, bool error) {
  809. // Release watchers in callback so that the leaked watchers of
  810. // the subdir stays. Otherwise, when the subdir is deleted,
  811. // its delete event would clean up leaked watchers in
  812. // `g_inotify_reader`.
  813. watcher.reset();
  814. run_loop.Quit();
  815. });
  816. bool setup_result = watcher->Watch(dir, FilePathWatcher::Type::kRecursive,
  817. watcher_callback);
  818. ASSERT_TRUE(setup_result);
  819. subdir_updater.task_runner()->PostTask(FROM_HERE, subdir_update_task);
  820. // Wait for the watch callback.
  821. run_loop.Run();
  822. // `watcher` should have been released.
  823. ASSERT_FALSE(watcher);
  824. // There should be no outstanding watchers.
  825. ASSERT_FALSE(FilePathWatcher::HasWatchesForTest());
  826. }
  827. }
  828. // Verify that "Watch()" returns false and callback is not invoked when limit is
  829. // hit during setup.
  830. TEST_F(FilePathWatcherTest, InotifyLimitInWatch) {
  831. auto watcher = std::make_unique<FilePathWatcher>();
  832. // "test_file()" is like "/tmp/__unique_path__/FilePathWatcherTest" and has 4
  833. // dir components ("/" + 3 named parts). "Watch()" creates inotify watches
  834. // for each dir component of the given dir. It would fail with limit set to 1.
  835. ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(1);
  836. ASSERT_FALSE(watcher->Watch(
  837. test_file(), FilePathWatcher::Type::kNonRecursive,
  838. base::BindLambdaForTesting(
  839. [&](const FilePath& path, bool error) { ADD_FAILURE(); })));
  840. // Triggers update but callback should not be invoked.
  841. ASSERT_TRUE(WriteFile(test_file(), "content"));
  842. // Ensures that the callback did not happen.
  843. base::RunLoop().RunUntilIdle();
  844. }
  845. // Verify that "error=true" callback happens when limit is hit during update.
  846. TEST_F(FilePathWatcherTest, InotifyLimitInUpdate) {
  847. enum kTestType {
  848. // Destroy watcher in "error=true" callback.
  849. // No crash/deadlock when releasing watcher in the callback.
  850. kDestroyWatcher,
  851. // Do not destroy watcher in "error=true" callback.
  852. kDoNothing,
  853. };
  854. for (auto callback_type : {kDestroyWatcher, kDoNothing}) {
  855. SCOPED_TRACE(testing::Message() << "type=" << callback_type);
  856. base::RunLoop run_loop;
  857. auto watcher = std::make_unique<FilePathWatcher>();
  858. bool error_callback_called = false;
  859. auto watcher_callback =
  860. base::BindLambdaForTesting([&](const FilePath& path, bool error) {
  861. // No callback should happen after "error=true" one.
  862. ASSERT_FALSE(error_callback_called);
  863. if (!error)
  864. return;
  865. error_callback_called = true;
  866. if (callback_type == kDestroyWatcher)
  867. watcher.reset();
  868. run_loop.Quit();
  869. });
  870. ASSERT_TRUE(watcher->Watch(
  871. test_file(), FilePathWatcher::Type::kNonRecursive, watcher_callback));
  872. ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(1);
  873. // Triggers update and over limit.
  874. ASSERT_TRUE(WriteFile(test_file(), "content"));
  875. run_loop.Run();
  876. // More update but no more callback should happen.
  877. ASSERT_TRUE(DeleteFile(test_file()));
  878. base::RunLoop().RunUntilIdle();
  879. }
  880. }
  881. // Similar to InotifyLimitInUpdate but test a recursive watcher.
  882. TEST_F(FilePathWatcherTest, InotifyLimitInUpdateRecursive) {
  883. enum kTestType {
  884. // Destroy watcher in "error=true" callback.
  885. // No crash/deadlock when releasing watcher in the callback.
  886. kDestroyWatcher,
  887. // Do not destroy watcher in "error=true" callback.
  888. kDoNothing,
  889. };
  890. FilePath dir(temp_dir_.GetPath().AppendASCII("dir"));
  891. for (auto callback_type : {kDestroyWatcher, kDoNothing}) {
  892. SCOPED_TRACE(testing::Message() << "type=" << callback_type);
  893. base::RunLoop run_loop;
  894. auto watcher = std::make_unique<FilePathWatcher>();
  895. bool error_callback_called = false;
  896. auto watcher_callback =
  897. base::BindLambdaForTesting([&](const FilePath& path, bool error) {
  898. // No callback should happen after "error=true" one.
  899. ASSERT_FALSE(error_callback_called);
  900. if (!error)
  901. return;
  902. error_callback_called = true;
  903. if (callback_type == kDestroyWatcher)
  904. watcher.reset();
  905. run_loop.Quit();
  906. });
  907. ASSERT_TRUE(watcher->Watch(dir, FilePathWatcher::Type::kRecursive,
  908. watcher_callback));
  909. constexpr size_t kMaxLimit = 10u;
  910. ScopedMaxNumberOfInotifyWatchesOverrideForTest max_inotify_watches(
  911. kMaxLimit);
  912. // Triggers updates and over limit.
  913. for (size_t i = 0; i < kMaxLimit; ++i) {
  914. base::FilePath subdir =
  915. dir.AppendASCII(base::StringPrintf("subdir_%" PRIuS, i));
  916. ASSERT_TRUE(CreateDirectory(subdir));
  917. }
  918. run_loop.Run();
  919. // More update but no more callback should happen.
  920. for (size_t i = 0; i < kMaxLimit; ++i) {
  921. base::FilePath subdir =
  922. dir.AppendASCII(base::StringPrintf("subdir_%" PRIuS, i));
  923. ASSERT_TRUE(DeleteFile(subdir));
  924. }
  925. base::RunLoop().RunUntilIdle();
  926. }
  927. }
  928. #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  929. enum Permission {
  930. Read,
  931. Write,
  932. Execute
  933. };
  934. #if BUILDFLAG(IS_APPLE)
  935. bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
  936. struct stat stat_buf;
  937. if (stat(path.value().c_str(), &stat_buf) != 0)
  938. return false;
  939. mode_t mode = 0;
  940. switch (perm) {
  941. case Read:
  942. mode = S_IRUSR | S_IRGRP | S_IROTH;
  943. break;
  944. case Write:
  945. mode = S_IWUSR | S_IWGRP | S_IWOTH;
  946. break;
  947. case Execute:
  948. mode = S_IXUSR | S_IXGRP | S_IXOTH;
  949. break;
  950. default:
  951. ADD_FAILURE() << "unknown perm " << perm;
  952. return false;
  953. }
  954. if (allow) {
  955. stat_buf.st_mode |= mode;
  956. } else {
  957. stat_buf.st_mode &= ~mode;
  958. }
  959. return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
  960. }
  961. #endif // BUILDFLAG(IS_APPLE)
  962. #if BUILDFLAG(IS_APPLE)
  963. // Linux implementation of FilePathWatcher doesn't catch attribute changes.
  964. // http://crbug.com/78043
  965. // Windows implementation of FilePathWatcher catches attribute changes that
  966. // don't affect the path being watched.
  967. // http://crbug.com/78045
  968. // Verify that changing attributes on a directory works.
  969. TEST_F(FilePathWatcherTest, DirAttributesChanged) {
  970. FilePath test_dir1(
  971. temp_dir_.GetPath().AppendASCII("DirAttributesChangedDir1"));
  972. FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
  973. FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
  974. // Setup a directory hierarchy.
  975. ASSERT_TRUE(base::CreateDirectory(test_dir1));
  976. ASSERT_TRUE(base::CreateDirectory(test_dir2));
  977. ASSERT_TRUE(WriteFile(test_file, "content"));
  978. FilePathWatcher watcher;
  979. std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
  980. ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get(),
  981. FilePathWatcher::Type::kNonRecursive));
  982. // We should not get notified in this case as it hasn't affected our ability
  983. // to access the file.
  984. ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
  985. ASSERT_FALSE(WaitForEventsWithTimeout(TestTimeouts::tiny_timeout()));
  986. ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
  987. // We should get notified in this case because filepathwatcher can no
  988. // longer access the file
  989. ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
  990. ASSERT_TRUE(WaitForEvents());
  991. ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
  992. }
  993. #endif // BUILDFLAG(IS_APPLE)
  994. #if BUILDFLAG(IS_MAC)
  995. // Fail fast if trying to trivially watch a non-existent item.
  996. TEST_F(FilePathWatcherTest, TrivialNoDir) {
  997. const FilePath tmp_dir = temp_dir_.GetPath();
  998. const FilePath non_existent = tmp_dir.Append(FILE_PATH_LITERAL("nope"));
  999. FilePathWatcher watcher;
  1000. auto delegate = std::make_unique<TestDelegate>(collector());
  1001. ASSERT_FALSE(SetupWatch(non_existent, &watcher, delegate.get(),
  1002. FilePathWatcher::Type::kTrivial));
  1003. }
  1004. // Succeed starting a watch on a directory.
  1005. TEST_F(FilePathWatcherTest, TrivialDirStart) {
  1006. const FilePath tmp_dir = temp_dir_.GetPath();
  1007. FilePathWatcher watcher;
  1008. auto delegate = std::make_unique<TestDelegate>(collector());
  1009. ASSERT_TRUE(SetupWatch(tmp_dir, &watcher, delegate.get(),
  1010. FilePathWatcher::Type::kTrivial));
  1011. }
  1012. // Observe a change on a directory
  1013. TEST_F(FilePathWatcherTest, TrivialDirChange) {
  1014. const FilePath tmp_dir = temp_dir_.GetPath();
  1015. FilePathWatcher watcher;
  1016. auto delegate = std::make_unique<TestDelegate>(collector());
  1017. ASSERT_TRUE(SetupWatch(tmp_dir, &watcher, delegate.get(),
  1018. FilePathWatcher::Type::kTrivial));
  1019. ASSERT_TRUE(TouchFile(tmp_dir, base::Time::Now(), base::Time::Now()));
  1020. ASSERT_TRUE(WaitForEvents());
  1021. }
  1022. // Observe no change when a parent is modified.
  1023. TEST_F(FilePathWatcherTest, TrivialParentDirChange) {
  1024. const FilePath tmp_dir = temp_dir_.GetPath();
  1025. const FilePath sub_dir1 = tmp_dir.Append(FILE_PATH_LITERAL("subdir"));
  1026. const FilePath sub_dir2 = sub_dir1.Append(FILE_PATH_LITERAL("subdir_redux"));
  1027. ASSERT_TRUE(CreateDirectory(sub_dir1));
  1028. ASSERT_TRUE(CreateDirectory(sub_dir2));
  1029. FilePathWatcher watcher;
  1030. auto delegate = std::make_unique<TestDelegate>(collector());
  1031. ASSERT_TRUE(SetupWatch(sub_dir2, &watcher, delegate.get(),
  1032. FilePathWatcher::Type::kTrivial));
  1033. // There should be no notification for a change to |sub_dir2|'s parent.
  1034. ASSERT_TRUE(Move(sub_dir1, tmp_dir.Append(FILE_PATH_LITERAL("over_here"))));
  1035. ASSERT_FALSE(WaitForEventsWithTimeout(TestTimeouts::tiny_timeout()));
  1036. }
  1037. // Do not crash when a directory is moved; https://crbug.com/1156603.
  1038. TEST_F(FilePathWatcherTest, TrivialDirMove) {
  1039. const FilePath tmp_dir = temp_dir_.GetPath();
  1040. const FilePath sub_dir = tmp_dir.Append(FILE_PATH_LITERAL("subdir"));
  1041. ASSERT_TRUE(CreateDirectory(sub_dir));
  1042. FilePathWatcher watcher;
  1043. auto delegate = std::make_unique<TestDelegate>(collector());
  1044. delegate->set_expect_error();
  1045. ASSERT_TRUE(SetupWatch(sub_dir, &watcher, delegate.get(),
  1046. FilePathWatcher::Type::kTrivial));
  1047. ASSERT_TRUE(Move(sub_dir, tmp_dir.Append(FILE_PATH_LITERAL("over_here"))));
  1048. ASSERT_TRUE(WaitForEvents());
  1049. }
  1050. #endif // BUILDFLAG(IS_MAC)
  1051. } // namespace
  1052. } // namespace base