Chromium Code Reviews| Index: chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc |
| diff --git a/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc b/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc |
| index 77ed95b51b41dd69f12aa378ea68b24b1c6b652d..ea946e94a9a974477dc3cbf7020eaac55c086155 100644 |
| --- a/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc |
| +++ b/chrome/browser/chromeos/file_system_provider/provided_file_system_unittest.cc |
| @@ -2,22 +2,26 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -// TODO(mtomasz): Move these test cases to operations/unmount_unittest.cc. |
| - |
| #include "chrome/browser/chromeos/file_system_provider/provided_file_system.h" |
| #include <string> |
| #include <vector> |
| #include "base/files/file.h" |
| +#include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| +#include "base/memory/scoped_vector.h" |
| #include "base/run_loop.h" |
| +#include "base/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" |
| +#include "chrome/browser/chromeos/file_system_provider/notification_manager.h" |
| #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" |
| #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" |
| +#include "chrome/browser/chromeos/file_system_provider/provided_file_system_observer.h" |
| #include "chrome/browser/chromeos/file_system_provider/request_manager.h" |
| #include "chrome/common/extensions/api/file_system_provider.h" |
| +#include "chrome/common/extensions/api/file_system_provider_internal.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "extensions/browser/event_router.h" |
| @@ -25,53 +29,159 @@ |
| namespace chromeos { |
| namespace file_system_provider { |
| - |
| namespace { |
| const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj"; |
| -const int kExpectedRequestId = 1; |
| const char kFileSystemId[] = "camera-pictures"; |
| const char kDisplayName[] = "Camera Pictures"; |
| +const char kDirectoryPath[] = "/hello/world"; |
|
hirono
2014/10/03 07:19:13
char -> base::FilePath::CharType ?
mtomasz
2014/10/03 08:35:34
Done.
mtomasz
2014/10/03 08:35:34
Done.
|
| +// Fake implementation of the event router, mocking out a real extension. |
| +// Handles requests and replies with fake answers back to the file system via |
| +// the request manager. |
| class FakeEventRouter : public extensions::EventRouter { |
| public: |
| - explicit FakeEventRouter(Profile* profile) : EventRouter(profile, NULL) {} |
| + FakeEventRouter(Profile* profile, ProvidedFileSystemInterface* file_system) |
| + : EventRouter(profile, NULL), |
| + file_system_(file_system), |
| + reply_result_(base::File::FILE_OK) {} |
| virtual ~FakeEventRouter() {} |
| + // Handles an event which would normally be routed to an extension. Instead |
| + // replies with a hard coded response. |
| virtual void DispatchEventToExtension(const std::string& extension_id, |
| scoped_ptr<extensions::Event> event) |
| OVERRIDE { |
| - extension_id_ = extension_id; |
| - event_ = event.Pass(); |
| + ASSERT_TRUE(file_system_); |
| + std::string file_system_id; |
| + const base::DictionaryValue* dictionary_value = NULL; |
| + ASSERT_TRUE(event->event_args->GetDictionary(0, &dictionary_value)); |
| + EXPECT_TRUE(dictionary_value->GetString("fileSystemId", &file_system_id)); |
| + EXPECT_EQ(kFileSystemId, file_system_id); |
| + int request_id = -1; |
| + EXPECT_TRUE(dictionary_value->GetInteger("requestId", &request_id)); |
| + EXPECT_TRUE(event->event_name == |
| + extensions::api::file_system_provider:: |
| + OnObserveDirectoryRequested::kEventName || |
| + event->event_name == extensions::api::file_system_provider:: |
| + OnUnobserveEntryRequested::kEventName); |
| + |
| + if (reply_result_ == base::File::FILE_OK) { |
| + base::ListValue value_as_list; |
| + value_as_list.Set(0, new base::StringValue(kFileSystemId)); |
| + value_as_list.Set(1, new base::FundamentalValue(request_id)); |
| + value_as_list.Set(2, new base::FundamentalValue(0) /* execution_time */); |
| + |
| + using extensions::api::file_system_provider_internal:: |
| + OperationRequestedSuccess::Params; |
| + scoped_ptr<Params> params(Params::Create(value_as_list)); |
| + ASSERT_TRUE(params.get()); |
| + file_system_->GetRequestManager()->FulfillRequest( |
| + request_id, |
| + RequestValue::CreateForOperationSuccess(params.Pass()), |
| + false /* has_more */); |
| + } else { |
| + file_system_->GetRequestManager()->RejectRequest( |
| + request_id, make_scoped_ptr(new RequestValue()), reply_result_); |
| + } |
| } |
| - const std::string& extension_id() const { return extension_id_; } |
| - |
| - const extensions::Event* event() const { return event_.get(); } |
| + void set_reply_result(base::File::Error result) { reply_result_ = result; } |
| private: |
| - std::string extension_id_; |
| - scoped_ptr<extensions::Event> event_; |
| - |
| + ProvidedFileSystemInterface* file_system_; // Not owned. |
|
hirono
2014/10/03 07:19:13
const?
mtomasz
2014/10/03 08:35:34
Done.
|
| + base::File::Error reply_result_; |
| DISALLOW_COPY_AND_ASSIGN(FakeEventRouter); |
| }; |
| -class EventLogger { |
| +// Observes the tested file system. |
| +class Observer : public ProvidedFileSystemObserver { |
| public: |
| - EventLogger() {} |
| - virtual ~EventLogger() {} |
| + class ChangeEvent { |
| + public: |
| + ChangeEvent(ProvidedFileSystemObserver::ChangeType change_type, |
| + const ProvidedFileSystemObserver::ChildChanges& child_changes) |
| + : change_type_(change_type), child_changes_(child_changes) {} |
| + virtual ~ChangeEvent() {} |
| + |
| + ProvidedFileSystemObserver::ChangeType change_type() const { |
| + return change_type_; |
| + } |
| + const ProvidedFileSystemObserver::ChildChanges& child_changes() const { |
| + return child_changes_; |
| + } |
| + |
| + private: |
| + ProvidedFileSystemObserver::ChangeType change_type_; |
|
hirono
2014/10/03 07:19:13
const?
mtomasz
2014/10/03 08:35:34
Done.
|
| + ProvidedFileSystemObserver::ChildChanges child_changes_; |
|
hirono
2014/10/03 07:19:13
const?
mtomasz
2014/10/03 08:35:33
Done.
|
| + |
| + DISALLOW_COPY_AND_ASSIGN(ChangeEvent); |
| + }; |
| + |
| + Observer() : list_changed_counter_(0), tag_updated_counter_(0) {} |
| + |
| + // ProvidedFileSystemInterfaceObserver overrides. |
| + virtual void OnObservedEntryChanged( |
| + const ProvidedFileSystemInfo& file_system_info, |
| + const base::FilePath& observed_path, |
| + ProvidedFileSystemObserver::ChangeType change_type, |
| + const ProvidedFileSystemObserver::ChildChanges& child_changes, |
| + const base::Closure& callback) OVERRIDE { |
| + EXPECT_EQ(kFileSystemId, file_system_info.file_system_id()); |
| + change_events_.push_back(new ChangeEvent(change_type, child_changes)); |
| + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); |
| + } |
| - void OnStatusCallback(base::File::Error error) { |
| - error_.reset(new base::File::Error(error)); |
| + virtual void OnObservedEntryTagUpdated( |
| + const ProvidedFileSystemInfo& file_system_info, |
| + const base::FilePath& observed_path) OVERRIDE { |
| + EXPECT_EQ(kFileSystemId, file_system_info.file_system_id()); |
| + ++tag_updated_counter_; |
| } |
| - base::File::Error* error() { return error_.get(); } |
| + virtual void OnObservedEntryListChanged( |
| + const ProvidedFileSystemInfo& file_system_info) OVERRIDE { |
| + EXPECT_EQ(kFileSystemId, file_system_info.file_system_id()); |
| + ++list_changed_counter_; |
| + } |
| + |
| + int list_changed_counter() const { return list_changed_counter_; } |
| + const ScopedVector<ChangeEvent>& change_events() const { |
| + return change_events_; |
| + } |
| + int tag_updated_counter() const { return tag_updated_counter_; } |
| private: |
| - scoped_ptr<base::File::Error> error_; |
| - DISALLOW_COPY_AND_ASSIGN(EventLogger); |
| + ScopedVector<ChangeEvent> change_events_; |
| + int list_changed_counter_; |
| + int tag_updated_counter_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Observer); |
| }; |
| +// Stub notification manager, which works in unit tests. |
| +class StubNotificationManager : public NotificationManagerInterface { |
| + public: |
| + StubNotificationManager() {} |
| + virtual ~StubNotificationManager() {} |
| + |
| + // NotificationManagerInterface overrides. |
| + virtual void ShowUnresponsiveNotification( |
| + int id, |
| + const NotificationCallback& callback) OVERRIDE {} |
| + virtual void HideUnresponsiveNotification(int id) OVERRIDE {} |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(StubNotificationManager); |
| +}; |
| + |
| +typedef std::vector<base::File::Error> Log; |
| + |
| +// Writes a |result| to the |log| vector. |
| +void LogStatus(Log* log, base::File::Error result) { |
| + log->push_back(result); |
| +} |
| + |
| } // namespace |
| class FileSystemProviderProvidedFileSystemTest : public testing::Test { |
| @@ -81,99 +191,420 @@ class FileSystemProviderProvidedFileSystemTest : public testing::Test { |
| virtual void SetUp() OVERRIDE { |
| profile_.reset(new TestingProfile); |
| - event_router_.reset(new FakeEventRouter(profile_.get())); |
| - event_router_->AddEventListener( |
| - extensions::api::file_system_provider::OnUnmountRequested::kEventName, |
| - NULL, |
| - kExtensionId); |
| - |
| const base::FilePath mount_path = |
| util::GetMountPath(profile_.get(), kExtensionId, kFileSystemId); |
| - file_system_info_.reset(new ProvidedFileSystemInfo( |
| - kExtensionId, kFileSystemId, kDisplayName, mount_path)); |
| + file_system_info_.reset( |
| + new ProvidedFileSystemInfo(kExtensionId, |
| + kFileSystemId, |
| + kDisplayName, |
| + false /* writable */, |
| + true /* supports_notify_tag */, |
| + mount_path)); |
| provided_file_system_.reset( |
| - new ProvidedFileSystem(event_router_.get(), *file_system_info_.get())); |
| + new ProvidedFileSystem(profile_.get(), *file_system_info_.get())); |
| + event_router_.reset( |
| + new FakeEventRouter(profile_.get(), provided_file_system_.get())); |
| + event_router_->AddEventListener(extensions::api::file_system_provider:: |
| + OnObserveDirectoryRequested::kEventName, |
| + NULL, |
| + kExtensionId); |
| + event_router_->AddEventListener(extensions::api::file_system_provider:: |
| + OnUnobserveEntryRequested::kEventName, |
| + NULL, |
| + kExtensionId); |
| + provided_file_system_->SetEventRouterForTesting(event_router_.get()); |
| + provided_file_system_->SetNotificationManagerForTesting( |
| + make_scoped_ptr(new StubNotificationManager)); |
| } |
| content::TestBrowserThreadBundle thread_bundle_; |
| scoped_ptr<TestingProfile> profile_; |
| scoped_ptr<FakeEventRouter> event_router_; |
| scoped_ptr<ProvidedFileSystemInfo> file_system_info_; |
| - scoped_ptr<ProvidedFileSystemInterface> provided_file_system_; |
| + scoped_ptr<ProvidedFileSystem> provided_file_system_; |
| }; |
| -TEST_F(FileSystemProviderProvidedFileSystemTest, RequestUnmount_Success) { |
| - EventLogger logger; |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, AutoUpdater) { |
| + Log log; |
| + base::Closure firstCallback; |
| + base::Closure secondCallback; |
| + |
| + { |
| + // Auto updater is ref counted, and bound to all callbacks. |
| + scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater( |
| + base::Bind(&LogStatus, base::Unretained(&log), base::File::FILE_OK))); |
| + |
| + firstCallback = auto_updater->CreateCallback(); |
| + secondCallback = auto_updater->CreateCallback(); |
| + } |
| + |
| + // Getting out of scope, should not invoke updating if there are pending |
| + // callbacks. |
| + EXPECT_EQ(0u, log.size()); |
| + |
| + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, firstCallback); |
| + base::RunLoop().RunUntilIdle(); |
| + EXPECT_EQ(0u, log.size()); |
| + |
| + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, secondCallback); |
| + base::RunLoop().RunUntilIdle(); |
| + EXPECT_EQ(1u, log.size()); |
| +} |
| + |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, AutoUpdater_NoCallbacks) { |
| + Log log; |
| + { |
| + scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater( |
| + base::Bind(&LogStatus, base::Unretained(&log), base::File::FILE_OK))); |
| + } |
| + EXPECT_EQ(1u, log.size()); |
| +} |
| + |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, AutoUpdater_CallbackIgnored) { |
| + Log log; |
| + { |
| + scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater( |
| + base::Bind(&LogStatus, base::Unretained(&log), base::File::FILE_OK))); |
| + base::Closure callback = auto_updater->CreateCallback(); |
| + // The callback gets out of scope, so the ref counted auto updater instance |
| + // gets deleted. Still, updating shouldn't be invoked, since the callback |
| + // wasn't executed. |
| + } |
| + base::RunLoop().RunUntilIdle(); |
| + EXPECT_EQ(0u, log.size()); |
| +} |
| + |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, ObserveDirectory_NotFound) { |
| + Log log; |
| + Observer observer; |
| + |
| + provided_file_system_->AddObserver(&observer); |
| - provided_file_system_->RequestUnmount( |
| - base::Bind(&EventLogger::OnStatusCallback, base::Unretained(logger))); |
| + // First, set the extension response to an error. |
| + event_router_->set_reply_result(base::File::FILE_ERROR_NOT_FOUND); |
| + |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| base::RunLoop().RunUntilIdle(); |
| - // Verify that the event has been sent to the providing extension. |
| - EXPECT_EQ(kExtensionId, event_router_->extension_id()); |
| - const extensions::Event* event = event_router_->event(); |
| - ASSERT_TRUE(event); |
| - ASSERT_TRUE(event->event_args); |
| - base::ListValue* event_args = event->event_args.get(); |
| - EXPECT_EQ(2u, event_args->GetSize()); |
| - std::string file_system_id; |
| - EXPECT_TRUE(event_args->GetString(0, &file_system_id)); |
| - EXPECT_EQ(kFileSystemId, file_system_id); |
| - |
| - // Remember the request id, and verify it is valid. |
| - int request_id = 0; |
| - EXPECT_TRUE(event_args->GetInteger(1, &request_id)); |
| - EXPECT_EQ(kExpectedRequestId, request_id); |
| - |
| - // Callback should not be called, yet. |
| - EXPECT_FALSE(logger.error()); |
| - |
| - // Simulate sending a success response from the providing extension. |
| - RequestManager* request_manager = provided_file_system_->GetRequestManager(); |
| - ASSERT_TRUE(request_manager); |
| - scoped_ptr<RequestValue> response; |
| - bool reply_result = request_manager->FulfillRequest( |
| - request_id, response.Pass(), false /* has_more */); |
| - EXPECT_TRUE(reply_result); |
| - |
| - // Callback should be called. Verify the error code. |
| - ASSERT_TRUE(logger.error()); |
| - EXPECT_EQ(base::File::FILE_OK, *logger.error()); |
| + // The directory should not become observed because of an error. |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0]); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(0u, observed_entries->size()); |
| + |
| + // The observer should not be called. |
| + EXPECT_EQ(0, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + provided_file_system_->RemoveObserver(&observer); |
| } |
| -TEST_F(FileSystemProviderProvidedFileSystemTest, RequestUnmount_Error) { |
| - EventLogger logger; |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, ObserveDirectory) { |
| + Log log; |
| + Observer observer; |
| + |
| + provided_file_system_->AddObserver(&observer); |
| - provided_file_system_->RequestUnmount( |
| - base::Bind(&EventLogger::OnStatusCallback, base::Unretained(logger))); |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| base::RunLoop().RunUntilIdle(); |
| - // Verify that the event has been sent to the providing extension. |
| - EXPECT_EQ(kExtensionId, event_router_->extension_id()); |
| - const extensions::Event* event = event_router_->event(); |
| - ASSERT_TRUE(event); |
| - ASSERT_TRUE(event->event_args); |
| - base::ListValue* event_args = event->event_args.get(); |
| - EXPECT_EQ(2u, event_args->GetSize()); |
| - std::string file_system_id; |
| - EXPECT_TRUE(event_args->GetString(0, &file_system_id)); |
| - EXPECT_EQ(kFileSystemId, file_system_id); |
| - |
| - // Remember the request id, and verify it is valid. |
| - int request_id = 0; |
| - EXPECT_TRUE(event_args->GetInteger(1, &request_id)); |
| - EXPECT_EQ(kExpectedRequestId, request_id); |
| - |
| - // Simulate sending an error response from the providing extension. |
| - RequestManager* request_manager = provided_file_system_->GetRequestManager(); |
| - ASSERT_TRUE(request_manager); |
| - bool reply_result = request_manager->RejectRequest( |
| - request_id, base::File::FILE_ERROR_NOT_FOUND); |
| - EXPECT_TRUE(reply_result); |
| - |
| - // Callback should be called. Verify the error code. |
| - ASSERT_TRUE(logger.error()); |
| - EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, *logger.error()); |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(1, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + ASSERT_EQ(1u, observed_entries->size()); |
| + const ProvidedFileSystemInterface::ObservedEntry& observed_entry = |
| + observed_entries->begin()->second; |
| + EXPECT_EQ(kDirectoryPath, observed_entry.entry_path.value()); |
| + EXPECT_FALSE(observed_entry.recursive); |
| + EXPECT_EQ("", observed_entry.last_tag); |
| + |
| + provided_file_system_->RemoveObserver(&observer); |
| +} |
| + |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, ObserveDirectory_Exists) { |
| + Observer observer; |
| + provided_file_system_->AddObserver(&observer); |
| + |
| + { |
| + // First observe a directory not recursively. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(1, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + } |
| + |
| + { |
| + // Create another non-recursive observer. That should fail. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_ERROR_EXISTS, log[0]); |
| + EXPECT_EQ(1, observer.list_changed_counter()); // No changes on the list. |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + } |
| + |
| + { |
| + // Create another observer on the same path, but a recursive one. That |
| + // should |
| + // succeed. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + true /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(2, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + ASSERT_EQ(1u, observed_entries->size()); |
| + const ProvidedFileSystemInterface::ObservedEntry& observed_entry = |
| + observed_entries->begin()->second; |
| + EXPECT_EQ(kDirectoryPath, observed_entry.entry_path.value()); |
| + EXPECT_TRUE(observed_entry.recursive); |
| + EXPECT_EQ("", observed_entry.last_tag); |
| + } |
| + |
| + { |
| + // Lastly, create another recursive observer. That should fail, too. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + true /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_ERROR_EXISTS, log[0]); |
| + EXPECT_EQ(2, observer.list_changed_counter()); // No changes on the list. |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + } |
| + |
| + provided_file_system_->RemoveObserver(&observer); |
| +} |
| + |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, UnobserveEntry) { |
| + Observer observer; |
| + provided_file_system_->AddObserver(&observer); |
| + |
| + { |
| + // First, confirm that unobserving an entry which is not observed, results |
| + // in an error. |
| + Log log; |
| + provided_file_system_->UnobserveEntry( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0]); |
| + EXPECT_EQ(0, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + } |
| + |
| + { |
| + // Observe a directory not recursively. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(1, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(1u, observed_entries->size()); |
| + } |
| + |
| + { |
| + // Unobserve it gracefully. |
| + Log log; |
| + provided_file_system_->UnobserveEntry( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(2, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(0u, observed_entries->size()); |
| + } |
| + |
| + { |
| + // Confirm that it's possible to observe it again. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(3, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(1u, observed_entries->size()); |
| + } |
| + |
| + { |
| + // Finally, unobserve it, but with an error from extension. That should |
| + // result |
| + // in a removed observer, anyway. |
| + event_router_->set_reply_result(base::File::FILE_ERROR_FAILED); |
| + |
| + Log log; |
| + provided_file_system_->UnobserveEntry( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_ERROR_FAILED, log[0]); |
| + EXPECT_EQ(4, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(0u, observed_entries->size()); |
| + } |
| + |
| + provided_file_system_->RemoveObserver(&observer); |
| +} |
| + |
| +TEST_F(FileSystemProviderProvidedFileSystemTest, Notify) { |
| + Observer observer; |
| + provided_file_system_->AddObserver(&observer); |
| + |
| + { |
| + // Observe a directory. |
| + Log log; |
| + provided_file_system_->ObserveDirectory( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + false /* recursive */, |
| + base::Bind(&LogStatus, base::Unretained(&log))); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + ASSERT_EQ(1u, log.size()); |
| + EXPECT_EQ(base::File::FILE_OK, log[0]); |
| + EXPECT_EQ(1, observer.list_changed_counter()); |
| + EXPECT_EQ(0, observer.tag_updated_counter()); |
| + |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(1u, observed_entries->size()); |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ("", observed_entries->begin()->second.last_tag); |
| + } |
| + |
| + { |
| + // Notify about a change. |
| + const ProvidedFileSystemObserver::ChangeType change_type = |
| + ProvidedFileSystemObserver::CHANGED; |
| + const ProvidedFileSystemObserver::ChildChanges child_changes; |
| + const std::string tag = "hello-world"; |
| + EXPECT_TRUE(provided_file_system_->Notify( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + change_type, |
| + child_changes, |
| + tag)); |
| + |
| + // Verify the observer event. |
| + ASSERT_EQ(1u, observer.change_events().size()); |
| + const Observer::ChangeEvent* const change_event = |
| + observer.change_events()[0]; |
| + EXPECT_EQ(change_type, change_event->change_type()); |
| + EXPECT_EQ(0u, change_event->child_changes().size()); |
| + |
| + // The tag should not be updated in advance, before all observers handle |
| + // the notification. |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(1u, observed_entries->size()); |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ("", observed_entries->begin()->second.last_tag); |
| + |
| + // Wait until all observers finish handling the notification. |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + // Confirm, that the entry is still being observed, and that the tag is |
| + // updated. |
| + ASSERT_EQ(1u, observed_entries->size()); |
| + EXPECT_EQ(tag, observed_entries->begin()->second.last_tag); |
| + EXPECT_EQ(1, observer.list_changed_counter()); |
| + EXPECT_EQ(1, observer.tag_updated_counter()); |
| + } |
| + |
| + { |
| + // Notify about deleting of the observed entry. |
| + const ProvidedFileSystemObserver::ChangeType change_type = |
| + ProvidedFileSystemObserver::DELETED; |
| + const ProvidedFileSystemObserver::ChildChanges child_changes; |
| + const std::string tag = "chocolate-disco"; |
| + EXPECT_TRUE(provided_file_system_->Notify( |
| + base::FilePath::FromUTF8Unsafe(kDirectoryPath), |
| + change_type, |
| + child_changes, |
| + tag)); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + // Verify the observer event. |
| + ASSERT_EQ(2u, observer.change_events().size()); |
| + const Observer::ChangeEvent* const change_event = |
| + observer.change_events()[1]; |
| + EXPECT_EQ(change_type, change_event->change_type()); |
| + EXPECT_EQ(0u, change_event->child_changes().size()); |
| + } |
| + |
| + // Confirm, that the entry is not observed anymore. |
| + { |
| + ProvidedFileSystemInterface::ObservedEntries* const observed_entries = |
| + provided_file_system_->GetObservedEntries(); |
| + EXPECT_EQ(0u, observed_entries->size()); |
| + EXPECT_EQ(2, observer.list_changed_counter()); |
| + EXPECT_EQ(2, observer.tag_updated_counter()); |
| + } |
| + |
| + provided_file_system_->RemoveObserver(&observer); |
| } |
| } // namespace file_system_provider |