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