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 |