1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
23345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Use of this source code is governed by a BSD-style license that can be
33345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// found in the LICENSE file.
43345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/files/file_path_watcher.h"
63345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
7731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <set>
83345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_WIN)
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <windows.h>
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <aclapi.h>
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#elif defined(OS_POSIX)
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <sys/stat.h>
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/basictypes.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/compiler_specific.h"
183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/file_path.h"
193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/file_util.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h"
213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/message_loop.h"
22731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/message_loop_proxy.h"
233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/path_service.h"
243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_util.h"
25731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/stl_util-inl.h"
263f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/synchronization/waitable_event.h"
27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/test/test_timeouts.h"
28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/threading/thread.h"
293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "testing/gtest/include/gtest/gtest.h"
303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace base {
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace files {
333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merricknamespace {
353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
36731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickclass TestDelegate;
37731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
38731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Aggregates notifications from the test delegates and breaks the message loop
39731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// the test thread is waiting on once they all came in.
40731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickclass NotificationCollector
41731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    : public base::RefCountedThreadSafe<NotificationCollector> {
42731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick public:
43731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  NotificationCollector()
44731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      : loop_(base::MessageLoopProxy::CreateForCurrentThread()) {}
45731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
46731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Called from the file thread by the delegates.
47731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  void OnChange(TestDelegate* delegate) {
48731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    loop_->PostTask(FROM_HERE,
49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                    NewRunnableMethod(this,
50731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                      &NotificationCollector::RecordChange,
51513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                      make_scoped_refptr(delegate)));
52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
54731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  void Register(TestDelegate* delegate) {
55731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    delegates_.insert(delegate);
56731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
57731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
58731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  void Reset() {
59731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    signaled_.clear();
60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool Success() {
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return signaled_ == delegates_;
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick private:
67731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  void RecordChange(TestDelegate* delegate) {
68731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ASSERT_TRUE(loop_->BelongsToCurrentThread());
69731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ASSERT_TRUE(delegates_.count(delegate));
70731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    signaled_.insert(delegate);
71731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
72731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Check whether all delegates have been signaled.
73731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (signaled_ == delegates_)
74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
75731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
76731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
77731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Set of registered delegates.
78731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::set<TestDelegate*> delegates_;
79731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
80731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Set of signaled delegates.
81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::set<TestDelegate*> signaled_;
82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // The loop we should break after all delegates signaled.
84731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<base::MessageLoopProxy> loop_;
85731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick};
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// A mock FilePathWatcher::Delegate for testing. I'd rather use gmock, but it's
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// not thread safe for setting expectations, so the test code couldn't safely
893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// reset expectations while the file watcher is running. In order to allow this,
90731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// we keep simple thread safe status flags in TestDelegate.
913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass TestDelegate : public FilePathWatcher::Delegate {
923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick public:
93731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // The message loop specified by |loop| will be quit if a notification is
94731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // received while the delegate is |armed_|. Note that the testing code must
95731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // guarantee |loop| outlives the file thread on which OnFilePathChanged runs.
96731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  explicit TestDelegate(NotificationCollector* collector)
97731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      : collector_(collector) {
98731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    collector_->Register(this);
993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  virtual void OnFilePathChanged(const FilePath&) {
102731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    collector_->OnChange(this);
1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnFilePathError(const FilePath& path) {
106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ADD_FAILURE() << "Error " << path.value();
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick private:
110731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<NotificationCollector> collector_;
1113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
1133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
1143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// A helper class for setting up watches on the file thread.
1163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass SetupWatchTask : public Task {
1173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick public:
1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  SetupWatchTask(const FilePath& target,
1193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 FilePathWatcher* watcher,
1203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 FilePathWatcher::Delegate* delegate,
1213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 bool* result,
1223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 base::WaitableEvent* completion)
1233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      : target_(target),
1243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        watcher_(watcher),
1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        delegate_(delegate),
1263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        result_(result),
1273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        completion_(completion) {}
1283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  void Run() {
1303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    *result_ = watcher_->Watch(target_, delegate_);
1313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    completion_->Signal();
1323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick private:
1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const FilePath target_;
1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher* watcher_;
1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher::Delegate* delegate_;
1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  bool* result_;
1393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  base::WaitableEvent* completion_;
1403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DISALLOW_COPY_AND_ASSIGN(SetupWatchTask);
1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
1433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass FilePathWatcherTest : public testing::Test {
1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick public:
1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcherTest()
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      : file_thread_("FilePathWatcherTest") {}
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual ~FilePathWatcherTest() {}
1503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick protected:
1523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  virtual void SetUp() {
1533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Create a separate file thread in order to test proper thread usage.
154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::Thread::Options options(MessageLoop::TYPE_IO, 0);
155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(file_thread_.StartWithOptions(options));
156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
157731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    collector_ = new NotificationCollector();
1583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  virtual void TearDown() {
1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    loop_.RunAllPending();
1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath test_file() {
165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return temp_dir_.path().AppendASCII("FilePathWatcherTest");
1663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Write |content| to |file|. Returns true on success.
1693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  bool WriteFile(const FilePath& file, const std::string& content) {
1703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    int write_size = file_util::WriteFile(file, content.c_str(),
1713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                          content.length());
1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return write_size == static_cast<int>(content.length());
1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool SetupWatch(const FilePath& target,
1763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                  FilePathWatcher* watcher,
177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  FilePathWatcher::Delegate* delegate) WARN_UNUSED_RESULT {
1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    base::WaitableEvent completion(false, false);
1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    bool result;
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    file_thread_.message_loop_proxy()->PostTask(FROM_HERE,
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen         new SetupWatchTask(target,
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            watcher,
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            delegate,
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            &result,
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            &completion));
1863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    completion.Wait();
187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return result;
1883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool WaitForEvents() WARN_UNUSED_RESULT {
191731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    collector_->Reset();
1923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    loop_.Run();
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return collector_->Success();
1943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
196731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  NotificationCollector* collector() { return collector_.get(); }
1973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  MessageLoop loop_;
199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::Thread file_thread_;
200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ScopedTempDir temp_dir_;
201731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<NotificationCollector> collector_;
2023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
2033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Basic test: Create the file and verify that we notice.
205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, NewFile) {
2063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
207731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
2093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
2123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Verify that modifying the file is caught.
215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, ModifiedFile) {
2163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
2173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
219731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
2213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Now make sure we get notified if the file is modified.
2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "new content"));
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
2253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Verify that moving the file into place is caught.
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, MovedFile) {
229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath source_file(temp_dir_.path().AppendASCII("source"));
2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(source_file, "content"));
2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
233731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
2353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Now make sure we get notified if the file is modified.
2373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::Move(source_file, test_file()));
238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
2393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, DeletedFile) {
2423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
2433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
245731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
2473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Now make sure we get notified if the file is deleted.
2493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  file_util::Delete(test_file(), false);
250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
2513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Used by the DeleteDuringNotify test below.
2543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Deletes the FilePathWatcher when it's notified.
2553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass Deleter : public FilePathWatcher::Delegate {
2563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick public:
2573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Deleter(FilePathWatcher* watcher, MessageLoop* loop)
2583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      : watcher_(watcher),
2593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        loop_(loop) {
2603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
2613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  virtual void OnFilePathChanged(const FilePath& path) {
263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    watcher_.reset();
2643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
2653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
2663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  scoped_ptr<FilePathWatcher> watcher_;
2683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  MessageLoop* loop_;
2693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
2703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Verify that deleting a watcher during the callback doesn't crash.
2723345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, DeleteDuringNotify) {
2733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher* watcher = new FilePathWatcher;
2743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Takes ownership of watcher.
2753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  scoped_refptr<Deleter> deleter(new Deleter(watcher, &loop_));
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), watcher, deleter.get()));
2773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
2803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We win if we haven't crashed yet.
2823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Might as well double-check it got deleted, too.
2833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(deleter->watcher_.get() == NULL);
2843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Verify that deleting the watcher works even if there is a pending
2873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// notification.
2883345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, DestroyWithPendingNotification) {
289731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
2903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher* watcher = new FilePathWatcher;
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), watcher, delegate.get()));
2923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  file_thread_.message_loop_proxy()->DeleteSoon(FROM_HERE, watcher);
2943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, MultipleWatchersSingleFile) {
2973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher1, watcher2;
298731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate1(new TestDelegate(collector()));
299731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate2(new TestDelegate(collector()));
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher1, delegate1.get()));
301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher2, delegate2.get()));
3023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
3063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Verify that watching a file whose parent directory doesn't exist yet works if
3083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// the directory and file are created eventually.
3093345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, NonExistentDirectory) {
3103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath dir(temp_dir_.path().AppendASCII("dir"));
3123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath file(dir.AppendASCII("file"));
313731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get()));
3153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::CreateDirectory(dir));
3173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file, "content"));
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
320513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file creation";
321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file, "content v2"));
324513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file change";
325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::Delete(file, false));
328513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file deletion";
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
3313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Exercises watch reconfiguration for the case that directories on the path
3333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// are rapidly created.
3343345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, DirectoryChain) {
335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath path(temp_dir_.path());
3363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  std::vector<std::string> dir_names;
3373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (int i = 0; i < 20; i++) {
3383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    std::string dir(StringPrintf("d%d", i));
3393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    dir_names.push_back(dir);
3403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    path = path.AppendASCII(dir);
3413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
3423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
3443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath file(path.AppendASCII("file"));
345731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get()));
3473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath sub_path(temp_dir_.path());
3493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (std::vector<std::string>::const_iterator d(dir_names.begin());
3503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick       d != dir_names.end(); ++d) {
3513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    sub_path = sub_path.AppendASCII(*d);
3523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    ASSERT_TRUE(file_util::CreateDirectory(sub_path));
3533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  VLOG(1) << "Create File";
3553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file, "content"));
356513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file creation";
357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file, "content v2"));
360513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file modification";
361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
3633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3643345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, DisappearingDirectory) {
3653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath dir(temp_dir_.path().AppendASCII("dir"));
3673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath file(dir.AppendASCII("file"));
3683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::CreateDirectory(dir));
3693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file, "content"));
370731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get()));
3723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::Delete(dir, true));
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
3763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Tests that a file that is deleted and reappears is tracked correctly.
3783345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, DeleteAndRecreate) {
3793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
3803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
381731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
3833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::Delete(test_file(), false));
385513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file deletion";
386ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(test_file(), "content"));
389513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file creation";
390ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
3913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
3923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3933345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, WatchDirectory) {
3943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher watcher;
395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath dir(temp_dir_.path().AppendASCII("dir"));
3963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath file1(dir.AppendASCII("file1"));
3973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath file2(dir.AppendASCII("file2"));
398731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
399ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get()));
4003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::CreateDirectory(dir));
402513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for directory creation";
403ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
4043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file1, "content"));
406513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file1 creation";
407ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
4083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
409ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if !defined(OS_MACOSX)
410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Mac implementation does not detect files modified in a directory.
4113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file1, "content v2"));
412513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file1 modification";
413ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
414ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // !OS_MACOSX
4153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::Delete(file1, false));
417513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file1 deletion";
418ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
4193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file2, "content"));
421513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file2 creation";
422ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
4233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
4243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4253345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, MoveParent) {
4263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher file_watcher;
4273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher subdir_watcher;
428ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath dir(temp_dir_.path().AppendASCII("dir"));
429ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath dest(temp_dir_.path().AppendASCII("dest"));
4303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath subdir(dir.AppendASCII("subdir"));
4313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath file(subdir.AppendASCII("file"));
432731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> file_delegate(new TestDelegate(collector()));
433ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(file, &file_watcher, file_delegate.get()));
434731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
435ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(subdir, &subdir_watcher, subdir_delegate.get()));
4363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Setup a directory hierarchy.
4383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::CreateDirectory(subdir));
4393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(file, "content"));
440513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for file creation";
441ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
4423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Move the parent directory.
4443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  file_util::Move(dir, dest);
445513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  VLOG(1) << "Waiting for directory move";
446ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
4473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
4483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4493345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickTEST_F(FilePathWatcherTest, MoveChild) {
4503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher file_watcher;
4513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePathWatcher subdir_watcher;
452ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath source_dir(temp_dir_.path().AppendASCII("source"));
4533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath source_subdir(source_dir.AppendASCII("subdir"));
4543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath source_file(source_subdir.AppendASCII("file"));
455ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath dest_dir(temp_dir_.path().AppendASCII("dest"));
4563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath dest_subdir(dest_dir.AppendASCII("subdir"));
4573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  FilePath dest_file(dest_subdir.AppendASCII("file"));
4583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Setup a directory hierarchy.
4603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::CreateDirectory(source_subdir));
4613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(WriteFile(source_file, "content"));
462731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
463731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> file_delegate(new TestDelegate(collector()));
464ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(dest_file, &file_watcher, file_delegate.get()));
465731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_refptr<TestDelegate> subdir_delegate(new TestDelegate(collector()));
466ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(dest_subdir, &subdir_watcher, subdir_delegate.get()));
4673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Move the directory into place, s.t. the watched file appears.
4693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ASSERT_TRUE(file_util::Move(source_dir, dest_dir));
470ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
471ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
473ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if !defined(OS_LINUX)
474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Linux implementation of FilePathWatcher doesn't catch attribute changes.
475ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// http://crbug.com/78043
476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Verify that changing attributes on a file is caught
478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, FileAttributesChanged) {
479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WriteFile(test_file(), "content"));
480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePathWatcher watcher;
481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file(), &watcher, delegate.get()));
483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Now make sure we get notified if the file is modified.
485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(file_util::MakeFileUnreadable(test_file()));
486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
487ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
488ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
489ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // !OS_LINUX
490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
491ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenenum Permission {
492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Read,
493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Write,
494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Execute
495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
498ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_POSIX)
499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  struct stat stat_buf;
500ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
501ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (stat(path.value().c_str(), &stat_buf) != 0)
502ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
503ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  mode_t mode = 0;
505ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  switch (perm) {
506ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case Read:
507ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      mode = S_IRUSR | S_IRGRP | S_IROTH;
508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case Write:
510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      mode = S_IWUSR | S_IWGRP | S_IWOTH;
511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
512ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case Execute:
513ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      mode = S_IXUSR | S_IXGRP | S_IXOTH;
514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    default:
516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ADD_FAILURE() << "unknown perm " << perm;
517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return false;
518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
519ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (allow) {
520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    stat_buf.st_mode |= mode;
521ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else {
522ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    stat_buf.st_mode &= ~mode;
523ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#elif defined(OS_WIN)
527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PACL old_dacl;
528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PSECURITY_DESCRIPTOR security_descriptor;
529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           SE_FILE_OBJECT,
531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           NULL, &security_descriptor) != ERROR_SUCCESS)
533ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
535ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DWORD mode = 0;
536ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  switch (perm) {
537ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case Read:
538ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      mode = GENERIC_READ;
539ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
540ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case Write:
541ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      mode = GENERIC_WRITE;
542ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
543ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case Execute:
544ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      mode = GENERIC_EXECUTE;
545ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
546ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    default:
547ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ADD_FAILURE() << "unknown perm " << perm;
548ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return false;
549ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
550ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
551ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Deny Read access for the current user.
552ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPLICIT_ACCESS change;
553ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.grfAccessPermissions = mode;
554ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.grfAccessMode = allow ? GRANT_ACCESS : DENY_ACCESS;
555ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.grfInheritance = 0;
556ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.Trustee.pMultipleTrustee = NULL;
557ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
558ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
559ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.Trustee.TrusteeType = TRUSTEE_IS_USER;
560ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  change.Trustee.ptstrName = L"CURRENT_USER";
561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PACL new_dacl;
563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    LocalFree(security_descriptor);
565ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
566ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
567ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
568ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
569ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                  SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
570ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                  NULL, NULL, new_dacl, NULL);
571ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  LocalFree(security_descriptor);
572ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  LocalFree(new_dacl);
573ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
574ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return rc == ERROR_SUCCESS;
575ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#else
576ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NOTIMPLEMENTED();
577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return false;
578ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif
579ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
580ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
581ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_MACOSX)
582ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Linux implementation of FilePathWatcher doesn't catch attribute changes.
583ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// http://crbug.com/78043
584ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Windows implementation of FilePathWatcher catches attribute changes that
585ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// don't affect the path being watched.
586ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// http://crbug.com/78045
587ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
588ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Verify that changing attributes on a directory works.
589ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(FilePathWatcherTest, DirAttributesChanged) {
590ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath test_dir1(temp_dir_.path().AppendASCII("DirAttributesChangedDir1"));
591ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
592ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
593ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Setup a directory hierarchy.
594ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(file_util::CreateDirectory(test_dir1));
595ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(file_util::CreateDirectory(test_dir2));
596ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WriteFile(test_file, "content"));
597ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
598ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePathWatcher watcher;
599ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_refptr<TestDelegate> delegate(new TestDelegate(collector()));
600ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(SetupWatch(test_file, &watcher, delegate.get()));
601ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
602ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // We should not get notified in this case as it hasn't affected our ability
603ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // to access the file.
604ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, false));
605ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  loop_.PostDelayedTask(FROM_HERE,
606ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                        new MessageLoop::QuitTask,
607ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                        TestTimeouts::tiny_timeout_ms());
608ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_FALSE(WaitForEvents());
609ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(ChangeFilePermissions(test_dir1, Read, true));
610ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
611ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // We should get notified in this case because filepathwatcher can no
612ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // longer access the file
613ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, false));
614ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(WaitForEvents());
615ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(ChangeFilePermissions(test_dir1, Execute, true));
6163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
6173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
618ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // OS_MACOSX
6193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}  // namespace
620ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
621ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}  // namespace files
622ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}  // namespace base
623