Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / file_system_provider / provided_file_system_unittest.cc
blob64be6fcc965c30af221fd4009e72950bbb1582ff
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/file_system_provider/provided_file_system.h"
7 #include <string>
8 #include <vector>
10 #include "base/files/file.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/run_loop.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/values.h"
17 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
18 #include "chrome/browser/chromeos/file_system_provider/notification_manager.h"
19 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
20 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
21 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_observer.h"
22 #include "chrome/browser/chromeos/file_system_provider/request_manager.h"
23 #include "chrome/browser/chromeos/file_system_provider/watcher.h"
24 #include "chrome/common/extensions/api/file_system_provider.h"
25 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
26 #include "chrome/common/extensions/api/file_system_provider_internal.h"
27 #include "chrome/test/base/testing_profile.h"
28 #include "content/public/test/test_browser_thread_bundle.h"
29 #include "extensions/browser/event_router.h"
30 #include "storage/browser/fileapi/watcher_manager.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 namespace chromeos {
34 namespace file_system_provider {
35 namespace {
37 const char kOrigin[] =
38 "chrome-extension://abcabcabcabcabcabcabcabcabcabcabcabca/";
39 const char kAnotherOrigin[] =
40 "chrome-extension://efgefgefgefgefgefgefgefgefgefgefgefge/";
41 const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
42 const char kFileSystemId[] = "camera-pictures";
43 const char kDisplayName[] = "Camera Pictures";
44 const base::FilePath::CharType kDirectoryPath[] =
45 FILE_PATH_LITERAL("/hello/world");
46 const base::FilePath::CharType kFilePath[] =
47 FILE_PATH_LITERAL("/welcome/to/my/world");
49 // Fake implementation of the event router, mocking out a real extension.
50 // Handles requests and replies with fake answers back to the file system via
51 // the request manager.
52 class FakeEventRouter : public extensions::EventRouter {
53 public:
54 FakeEventRouter(Profile* profile, ProvidedFileSystemInterface* file_system)
55 : EventRouter(profile, NULL),
56 file_system_(file_system),
57 reply_result_(base::File::FILE_OK) {}
58 ~FakeEventRouter() override {}
60 // Handles an event which would normally be routed to an extension. Instead
61 // replies with a hard coded response.
62 void DispatchEventToExtension(const std::string& extension_id,
63 scoped_ptr<extensions::Event> event) override {
64 ASSERT_TRUE(file_system_);
65 std::string file_system_id;
66 const base::DictionaryValue* dictionary_value = NULL;
67 ASSERT_TRUE(event->event_args->GetDictionary(0, &dictionary_value));
68 EXPECT_TRUE(dictionary_value->GetString("fileSystemId", &file_system_id));
69 EXPECT_EQ(kFileSystemId, file_system_id);
70 int request_id = -1;
71 EXPECT_TRUE(dictionary_value->GetInteger("requestId", &request_id));
72 EXPECT_TRUE(event->event_name == extensions::api::file_system_provider::
73 OnAddWatcherRequested::kEventName ||
74 event->event_name == extensions::api::file_system_provider::
75 OnRemoveWatcherRequested::kEventName ||
76 event->event_name == extensions::api::file_system_provider::
77 OnOpenFileRequested::kEventName ||
78 event->event_name == extensions::api::file_system_provider::
79 OnCloseFileRequested::kEventName);
81 if (reply_result_ == base::File::FILE_OK) {
82 base::ListValue value_as_list;
83 value_as_list.Set(0, new base::StringValue(kFileSystemId));
84 value_as_list.Set(1, new base::FundamentalValue(request_id));
85 value_as_list.Set(2, new base::FundamentalValue(0) /* execution_time */);
87 using extensions::api::file_system_provider_internal::
88 OperationRequestedSuccess::Params;
89 scoped_ptr<Params> params(Params::Create(value_as_list));
90 ASSERT_TRUE(params.get());
91 file_system_->GetRequestManager()->FulfillRequest(
92 request_id,
93 RequestValue::CreateForOperationSuccess(params.Pass()),
94 false /* has_more */);
95 } else {
96 file_system_->GetRequestManager()->RejectRequest(
97 request_id, make_scoped_ptr(new RequestValue()), reply_result_);
101 void set_reply_result(base::File::Error result) { reply_result_ = result; }
103 private:
104 ProvidedFileSystemInterface* const file_system_; // Not owned.
105 base::File::Error reply_result_;
106 DISALLOW_COPY_AND_ASSIGN(FakeEventRouter);
109 // Observes the tested file system.
110 class Observer : public ProvidedFileSystemObserver {
111 public:
112 class ChangeEvent {
113 public:
114 ChangeEvent(storage::WatcherManager::ChangeType change_type,
115 const ProvidedFileSystemObserver::Changes& changes)
116 : change_type_(change_type), changes_(changes) {}
117 virtual ~ChangeEvent() {}
119 storage::WatcherManager::ChangeType change_type() const {
120 return change_type_;
122 const ProvidedFileSystemObserver::Changes& changes() const {
123 return changes_;
126 private:
127 const storage::WatcherManager::ChangeType change_type_;
128 const ProvidedFileSystemObserver::Changes changes_;
130 DISALLOW_COPY_AND_ASSIGN(ChangeEvent);
133 Observer() : list_changed_counter_(0), tag_updated_counter_(0) {}
135 // ProvidedFileSystemInterfaceObserver overrides.
136 void OnWatcherChanged(const ProvidedFileSystemInfo& file_system_info,
137 const Watcher& watcher,
138 storage::WatcherManager::ChangeType change_type,
139 const ProvidedFileSystemObserver::Changes& changes,
140 const base::Closure& callback) override {
141 EXPECT_EQ(kFileSystemId, file_system_info.file_system_id());
142 change_events_.push_back(new ChangeEvent(change_type, changes));
143 complete_callback_ = callback;
146 void OnWatcherTagUpdated(const ProvidedFileSystemInfo& file_system_info,
147 const Watcher& watcher) override {
148 EXPECT_EQ(kFileSystemId, file_system_info.file_system_id());
149 ++tag_updated_counter_;
152 void OnWatcherListChanged(const ProvidedFileSystemInfo& file_system_info,
153 const Watchers& watchers) override {
154 EXPECT_EQ(kFileSystemId, file_system_info.file_system_id());
155 ++list_changed_counter_;
158 // Completes handling the OnWatcherChanged event.
159 void CompleteOnWatcherChanged() {
160 DCHECK(!complete_callback_.is_null());
161 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
162 complete_callback_);
163 complete_callback_ = base::Closure();
166 int list_changed_counter() const { return list_changed_counter_; }
167 const ScopedVector<ChangeEvent>& change_events() const {
168 return change_events_;
170 int tag_updated_counter() const { return tag_updated_counter_; }
172 private:
173 ScopedVector<ChangeEvent> change_events_;
174 int list_changed_counter_;
175 int tag_updated_counter_;
176 base::Closure complete_callback_;
178 DISALLOW_COPY_AND_ASSIGN(Observer);
181 // Stub notification manager, which works in unit tests.
182 class StubNotificationManager : public NotificationManagerInterface {
183 public:
184 StubNotificationManager() {}
185 ~StubNotificationManager() override {}
187 // NotificationManagerInterface overrides.
188 void ShowUnresponsiveNotification(
189 int id,
190 const NotificationCallback& callback) override {}
191 void HideUnresponsiveNotification(int id) override {}
193 private:
194 DISALLOW_COPY_AND_ASSIGN(StubNotificationManager);
197 typedef std::vector<base::File::Error> Log;
198 typedef std::vector<storage::WatcherManager::ChangeType> NotificationLog;
199 typedef std::vector<std::pair<int, base::File::Error>> OpenFileLog;
201 // Writes a |result| to the |log| vector.
202 void LogStatus(Log* log, base::File::Error result) {
203 log->push_back(result);
206 // Writes a |change_type| to the |notification_log| vector.
207 void LogNotification(NotificationLog* notification_log,
208 storage::WatcherManager::ChangeType change_type) {
209 notification_log->push_back(change_type);
212 // Writes a |file_handle| and |result| to the |open_file_log| vector.
213 void LogOpenFile(OpenFileLog* open_file_log,
214 int file_handle,
215 base::File::Error result) {
216 open_file_log->push_back(std::make_pair(file_handle, result));
219 } // namespace
221 class FileSystemProviderProvidedFileSystemTest : public testing::Test {
222 protected:
223 FileSystemProviderProvidedFileSystemTest() {}
224 ~FileSystemProviderProvidedFileSystemTest() override {}
226 void SetUp() override {
227 profile_.reset(new TestingProfile);
228 const base::FilePath mount_path =
229 util::GetMountPath(profile_.get(), kExtensionId, kFileSystemId);
230 MountOptions mount_options;
231 mount_options.file_system_id = kFileSystemId;
232 mount_options.display_name = kDisplayName;
233 mount_options.supports_notify_tag = true;
234 mount_options.writable = true;
235 file_system_info_.reset(new ProvidedFileSystemInfo(
236 kExtensionId, mount_options, mount_path, false /* configurable */,
237 true /* watchable */, extensions::SOURCE_FILE));
238 provided_file_system_.reset(
239 new ProvidedFileSystem(profile_.get(), *file_system_info_.get()));
240 event_router_.reset(
241 new FakeEventRouter(profile_.get(), provided_file_system_.get()));
242 event_router_->AddEventListener(extensions::api::file_system_provider::
243 OnAddWatcherRequested::kEventName,
244 NULL,
245 kExtensionId);
246 event_router_->AddEventListener(extensions::api::file_system_provider::
247 OnRemoveWatcherRequested::kEventName,
248 NULL,
249 kExtensionId);
250 event_router_->AddEventListener(
251 extensions::api::file_system_provider::OnOpenFileRequested::kEventName,
252 NULL, kExtensionId);
253 event_router_->AddEventListener(
254 extensions::api::file_system_provider::OnCloseFileRequested::kEventName,
255 NULL, kExtensionId);
256 provided_file_system_->SetEventRouterForTesting(event_router_.get());
257 provided_file_system_->SetNotificationManagerForTesting(
258 make_scoped_ptr(new StubNotificationManager));
261 content::TestBrowserThreadBundle thread_bundle_;
262 scoped_ptr<TestingProfile> profile_;
263 scoped_ptr<FakeEventRouter> event_router_;
264 scoped_ptr<ProvidedFileSystemInfo> file_system_info_;
265 scoped_ptr<ProvidedFileSystem> provided_file_system_;
268 TEST_F(FileSystemProviderProvidedFileSystemTest, AutoUpdater) {
269 Log log;
270 base::Closure firstCallback;
271 base::Closure secondCallback;
274 // Auto updater is ref counted, and bound to all callbacks.
275 scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater(
276 base::Bind(&LogStatus, base::Unretained(&log), base::File::FILE_OK)));
278 firstCallback = auto_updater->CreateCallback();
279 secondCallback = auto_updater->CreateCallback();
282 // Getting out of scope, should not invoke updating if there are pending
283 // callbacks.
284 EXPECT_EQ(0u, log.size());
286 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, firstCallback);
287 base::RunLoop().RunUntilIdle();
288 EXPECT_EQ(0u, log.size());
290 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, secondCallback);
291 base::RunLoop().RunUntilIdle();
292 EXPECT_EQ(1u, log.size());
295 TEST_F(FileSystemProviderProvidedFileSystemTest, AutoUpdater_NoCallbacks) {
296 Log log;
298 scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater(
299 base::Bind(&LogStatus, base::Unretained(&log), base::File::FILE_OK)));
301 EXPECT_EQ(1u, log.size());
304 TEST_F(FileSystemProviderProvidedFileSystemTest, AutoUpdater_CallbackIgnored) {
305 Log log;
307 scoped_refptr<AutoUpdater> auto_updater(new AutoUpdater(
308 base::Bind(&LogStatus, base::Unretained(&log), base::File::FILE_OK)));
309 base::Closure callback = auto_updater->CreateCallback();
310 // The callback gets out of scope, so the ref counted auto updater instance
311 // gets deleted. Still, updating shouldn't be invoked, since the callback
312 // wasn't executed.
314 base::RunLoop().RunUntilIdle();
315 EXPECT_EQ(0u, log.size());
318 TEST_F(FileSystemProviderProvidedFileSystemTest, AddWatcher_NotFound) {
319 Log log;
320 NotificationLog notification_log;
321 Observer observer;
323 provided_file_system_->AddObserver(&observer);
325 // First, set the extension response to an error.
326 event_router_->set_reply_result(base::File::FILE_ERROR_NOT_FOUND);
328 provided_file_system_->AddWatcher(
329 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
330 false /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
331 base::Bind(&LogNotification, base::Unretained(&notification_log)));
332 base::RunLoop().RunUntilIdle();
334 // The directory should not become watched because of an error.
335 ASSERT_EQ(1u, log.size());
336 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0]);
337 EXPECT_EQ(0u, notification_log.size());
339 Watchers* const watchers = provided_file_system_->GetWatchers();
340 EXPECT_EQ(0u, watchers->size());
342 // The observer should not be called.
343 EXPECT_EQ(0, observer.list_changed_counter());
344 EXPECT_EQ(0, observer.tag_updated_counter());
346 provided_file_system_->RemoveObserver(&observer);
349 TEST_F(FileSystemProviderProvidedFileSystemTest, AddWatcher) {
350 Log log;
351 Observer observer;
353 provided_file_system_->AddObserver(&observer);
355 provided_file_system_->AddWatcher(
356 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
357 true /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
358 storage::WatcherManager::NotificationCallback());
359 base::RunLoop().RunUntilIdle();
361 ASSERT_EQ(1u, log.size());
362 EXPECT_EQ(base::File::FILE_OK, log[0]);
363 EXPECT_EQ(1, observer.list_changed_counter());
364 EXPECT_EQ(0, observer.tag_updated_counter());
366 Watchers* const watchers = provided_file_system_->GetWatchers();
367 ASSERT_EQ(1u, watchers->size());
368 const Watcher& watcher = watchers->begin()->second;
369 EXPECT_EQ(FILE_PATH_LITERAL(kDirectoryPath), watcher.entry_path.value());
370 EXPECT_FALSE(watcher.recursive);
371 EXPECT_EQ("", watcher.last_tag);
373 provided_file_system_->RemoveObserver(&observer);
376 TEST_F(FileSystemProviderProvidedFileSystemTest, AddWatcher_PersistentIllegal) {
378 // Adding a persistent watcher with a notification callback is not allowed,
379 // as it's basically impossible to restore the callback after a shutdown.
380 Log log;
381 NotificationLog notification_log;
383 Observer observer;
384 provided_file_system_->AddObserver(&observer);
386 provided_file_system_->AddWatcher(
387 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
388 true /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
389 base::Bind(&LogNotification, base::Unretained(&notification_log)));
390 base::RunLoop().RunUntilIdle();
392 ASSERT_EQ(1u, log.size());
393 EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, log[0]);
394 EXPECT_EQ(0, observer.list_changed_counter());
395 EXPECT_EQ(0, observer.tag_updated_counter());
397 provided_file_system_->RemoveObserver(&observer);
401 // Adding a persistent watcher is not allowed if the file system doesn't
402 // support the notify tag. It's because the notify tag is essential to be
403 // able to recreate notification during shutdown.
404 Log log;
405 Observer observer;
407 // Create a provided file system interface, which does not support a notify
408 // tag, though.
409 const base::FilePath mount_path =
410 util::GetMountPath(profile_.get(), kExtensionId, kFileSystemId);
411 MountOptions mount_options;
412 mount_options.file_system_id = kFileSystemId;
413 mount_options.display_name = kDisplayName;
414 mount_options.supports_notify_tag = false;
415 ProvidedFileSystemInfo file_system_info(
416 kExtensionId, mount_options, mount_path, false /* configurable */,
417 true /* watchable */, extensions::SOURCE_FILE);
418 ProvidedFileSystem simple_provided_file_system(profile_.get(),
419 file_system_info);
420 simple_provided_file_system.SetEventRouterForTesting(event_router_.get());
421 simple_provided_file_system.SetNotificationManagerForTesting(
422 make_scoped_ptr(new StubNotificationManager));
424 simple_provided_file_system.AddObserver(&observer);
426 simple_provided_file_system.AddWatcher(
427 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
428 true /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
429 storage::WatcherManager::NotificationCallback());
430 base::RunLoop().RunUntilIdle();
432 ASSERT_EQ(1u, log.size());
433 EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, log[0]);
434 EXPECT_EQ(0, observer.list_changed_counter());
435 EXPECT_EQ(0, observer.tag_updated_counter());
437 simple_provided_file_system.RemoveObserver(&observer);
441 TEST_F(FileSystemProviderProvidedFileSystemTest, AddWatcher_Exists) {
442 Observer observer;
443 provided_file_system_->AddObserver(&observer);
446 // First watch a directory not recursively.
447 Log log;
448 provided_file_system_->AddWatcher(
449 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
450 true /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
451 storage::WatcherManager::NotificationCallback());
452 base::RunLoop().RunUntilIdle();
454 ASSERT_EQ(1u, log.size());
455 EXPECT_EQ(base::File::FILE_OK, log[0]);
456 EXPECT_EQ(1, observer.list_changed_counter());
457 EXPECT_EQ(0, observer.tag_updated_counter());
459 Watchers* const watchers = provided_file_system_->GetWatchers();
460 ASSERT_TRUE(watchers);
461 ASSERT_EQ(1u, watchers->size());
462 const auto& watcher_it = watchers->find(
463 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath)),
464 false /* recursive */));
465 ASSERT_NE(watchers->end(), watcher_it);
467 EXPECT_EQ(1u, watcher_it->second.subscribers.size());
468 const auto& subscriber_it =
469 watcher_it->second.subscribers.find(GURL(kOrigin));
470 ASSERT_NE(watcher_it->second.subscribers.end(), subscriber_it);
471 EXPECT_EQ(kOrigin, subscriber_it->second.origin.spec());
472 EXPECT_TRUE(subscriber_it->second.persistent);
476 // Create another non-recursive observer. That should fail.
477 Log log;
478 provided_file_system_->AddWatcher(
479 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
480 true /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
481 storage::WatcherManager::NotificationCallback());
482 base::RunLoop().RunUntilIdle();
484 ASSERT_EQ(1u, log.size());
485 EXPECT_EQ(base::File::FILE_ERROR_EXISTS, log[0]);
486 EXPECT_EQ(1, observer.list_changed_counter()); // No changes on the list.
487 EXPECT_EQ(0, observer.tag_updated_counter());
491 // Lastly, create another recursive observer. That should succeed.
492 Log log;
493 provided_file_system_->AddWatcher(
494 GURL(kOrigin), base::FilePath(kDirectoryPath), true /* recursive */,
495 true /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
496 storage::WatcherManager::NotificationCallback());
497 base::RunLoop().RunUntilIdle();
499 ASSERT_EQ(1u, log.size());
500 EXPECT_EQ(base::File::FILE_OK, log[0]);
501 EXPECT_EQ(2, observer.list_changed_counter());
502 EXPECT_EQ(0, observer.tag_updated_counter());
505 provided_file_system_->RemoveObserver(&observer);
508 TEST_F(FileSystemProviderProvidedFileSystemTest, AddWatcher_MultipleOrigins) {
509 Observer observer;
510 provided_file_system_->AddObserver(&observer);
513 // First watch a directory not recursively.
514 Log log;
515 NotificationLog notification_log;
517 provided_file_system_->AddWatcher(
518 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
519 false /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
520 base::Bind(&LogNotification, base::Unretained(&notification_log)));
521 base::RunLoop().RunUntilIdle();
523 ASSERT_EQ(1u, log.size());
524 EXPECT_EQ(base::File::FILE_OK, log[0]);
525 EXPECT_EQ(1, observer.list_changed_counter());
526 EXPECT_EQ(0, observer.tag_updated_counter());
527 EXPECT_EQ(0u, notification_log.size());
529 Watchers* const watchers = provided_file_system_->GetWatchers();
530 ASSERT_TRUE(watchers);
531 ASSERT_EQ(1u, watchers->size());
532 const auto& watcher_it = watchers->find(
533 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath)),
534 false /* recursive */));
535 ASSERT_NE(watchers->end(), watcher_it);
537 EXPECT_EQ(1u, watcher_it->second.subscribers.size());
538 const auto& subscriber_it =
539 watcher_it->second.subscribers.find(GURL(kOrigin));
540 ASSERT_NE(watcher_it->second.subscribers.end(), subscriber_it);
541 EXPECT_EQ(kOrigin, subscriber_it->first.spec());
542 EXPECT_EQ(kOrigin, subscriber_it->second.origin.spec());
543 EXPECT_FALSE(subscriber_it->second.persistent);
547 // Create another watcher, but recursive and with a different origin.
548 Log log;
549 NotificationLog notification_log;
551 provided_file_system_->AddWatcher(
552 GURL(kAnotherOrigin), base::FilePath(kDirectoryPath),
553 true /* recursive */, false /* persistent */,
554 base::Bind(&LogStatus, base::Unretained(&log)),
555 base::Bind(&LogNotification, base::Unretained(&notification_log)));
556 base::RunLoop().RunUntilIdle();
558 ASSERT_EQ(1u, log.size());
559 EXPECT_EQ(base::File::FILE_OK, log[0]);
560 EXPECT_EQ(2, observer.list_changed_counter());
561 EXPECT_EQ(0, observer.tag_updated_counter());
562 EXPECT_EQ(0u, notification_log.size());
564 Watchers* const watchers = provided_file_system_->GetWatchers();
565 ASSERT_TRUE(watchers);
566 ASSERT_EQ(2u, watchers->size());
567 const auto& watcher_it = watchers->find(
568 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath)),
569 false /* recursive */));
570 ASSERT_NE(watchers->end(), watcher_it);
572 EXPECT_EQ(1u, watcher_it->second.subscribers.size());
573 const auto& subscriber_it =
574 watcher_it->second.subscribers.find(GURL(kOrigin));
575 ASSERT_NE(watcher_it->second.subscribers.end(), subscriber_it);
576 EXPECT_EQ(kOrigin, subscriber_it->first.spec());
577 EXPECT_EQ(kOrigin, subscriber_it->second.origin.spec());
578 EXPECT_FALSE(subscriber_it->second.persistent);
582 // Remove the second watcher gracefully.
583 Log log;
584 provided_file_system_->RemoveWatcher(
585 GURL(kAnotherOrigin), base::FilePath(kDirectoryPath),
586 true /* recursive */, base::Bind(&LogStatus, base::Unretained(&log)));
587 base::RunLoop().RunUntilIdle();
589 ASSERT_EQ(1u, log.size());
590 EXPECT_EQ(base::File::FILE_OK, log[0]);
591 EXPECT_EQ(3, observer.list_changed_counter());
592 EXPECT_EQ(0, observer.tag_updated_counter());
594 Watchers* const watchers = provided_file_system_->GetWatchers();
595 ASSERT_TRUE(watchers);
596 EXPECT_EQ(1u, watchers->size());
597 const auto& watcher_it = watchers->find(
598 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath)),
599 false /* recursive */));
600 ASSERT_NE(watchers->end(), watcher_it);
602 EXPECT_EQ(1u, watcher_it->second.subscribers.size());
603 const auto& subscriber_it =
604 watcher_it->second.subscribers.find(GURL(kOrigin));
605 ASSERT_NE(watcher_it->second.subscribers.end(), subscriber_it);
606 EXPECT_EQ(kOrigin, subscriber_it->first.spec());
607 EXPECT_EQ(kOrigin, subscriber_it->second.origin.spec());
608 EXPECT_FALSE(subscriber_it->second.persistent);
611 provided_file_system_->RemoveObserver(&observer);
614 TEST_F(FileSystemProviderProvidedFileSystemTest, RemoveWatcher) {
615 Observer observer;
616 provided_file_system_->AddObserver(&observer);
619 // First, confirm that removing a watcher which does not exist results in an
620 // error.
621 Log log;
622 provided_file_system_->RemoveWatcher(
623 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
624 base::Bind(&LogStatus, base::Unretained(&log)));
625 base::RunLoop().RunUntilIdle();
627 ASSERT_EQ(1u, log.size());
628 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0]);
629 EXPECT_EQ(0, observer.list_changed_counter());
630 EXPECT_EQ(0, observer.tag_updated_counter());
634 // Watch a directory not recursively.
635 Log log;
636 NotificationLog notification_log;
638 provided_file_system_->AddWatcher(
639 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
640 false /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
641 base::Bind(&LogNotification, base::Unretained(&notification_log)));
642 base::RunLoop().RunUntilIdle();
644 ASSERT_EQ(1u, log.size());
645 EXPECT_EQ(base::File::FILE_OK, log[0]);
646 EXPECT_EQ(1, observer.list_changed_counter());
647 EXPECT_EQ(0, observer.tag_updated_counter());
648 EXPECT_EQ(0u, notification_log.size());
650 Watchers* const watchers = provided_file_system_->GetWatchers();
651 EXPECT_EQ(1u, watchers->size());
655 // Remove a watcher gracefully.
656 Log log;
657 provided_file_system_->RemoveWatcher(
658 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
659 base::Bind(&LogStatus, base::Unretained(&log)));
660 base::RunLoop().RunUntilIdle();
662 ASSERT_EQ(1u, log.size());
663 EXPECT_EQ(base::File::FILE_OK, log[0]);
664 EXPECT_EQ(2, observer.list_changed_counter());
665 EXPECT_EQ(0, observer.tag_updated_counter());
667 Watchers* const watchers = provided_file_system_->GetWatchers();
668 EXPECT_EQ(0u, watchers->size());
672 // Confirm that it's possible to watch it again.
673 Log log;
674 NotificationLog notification_log;
676 provided_file_system_->AddWatcher(
677 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
678 false /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
679 base::Bind(&LogNotification, base::Unretained(&notification_log)));
680 base::RunLoop().RunUntilIdle();
682 ASSERT_EQ(1u, log.size());
683 EXPECT_EQ(base::File::FILE_OK, log[0]);
684 EXPECT_EQ(3, observer.list_changed_counter());
685 EXPECT_EQ(0, observer.tag_updated_counter());
686 EXPECT_EQ(0u, notification_log.size());
688 Watchers* const watchers = provided_file_system_->GetWatchers();
689 EXPECT_EQ(1u, watchers->size());
693 // Finally, remove it, but with an error from extension. That should result
694 // in a removed watcher, anyway. The error code should not be passed.
695 event_router_->set_reply_result(base::File::FILE_ERROR_FAILED);
697 Log log;
698 provided_file_system_->RemoveWatcher(
699 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
700 base::Bind(&LogStatus, base::Unretained(&log)));
701 base::RunLoop().RunUntilIdle();
703 ASSERT_EQ(1u, log.size());
704 EXPECT_EQ(base::File::FILE_OK, log[0]);
705 EXPECT_EQ(4, observer.list_changed_counter());
706 EXPECT_EQ(0, observer.tag_updated_counter());
708 Watchers* const watchers = provided_file_system_->GetWatchers();
709 EXPECT_EQ(0u, watchers->size());
712 provided_file_system_->RemoveObserver(&observer);
715 TEST_F(FileSystemProviderProvidedFileSystemTest, Notify) {
716 Observer observer;
717 provided_file_system_->AddObserver(&observer);
718 NotificationLog notification_log;
721 // Watch a directory.
722 Log log;
724 provided_file_system_->AddWatcher(
725 GURL(kOrigin), base::FilePath(kDirectoryPath), false /* recursive */,
726 false /* persistent */, base::Bind(&LogStatus, base::Unretained(&log)),
727 base::Bind(&LogNotification, base::Unretained(&notification_log)));
728 base::RunLoop().RunUntilIdle();
730 ASSERT_EQ(1u, log.size());
731 EXPECT_EQ(base::File::FILE_OK, log[0]);
732 EXPECT_EQ(1, observer.list_changed_counter());
733 EXPECT_EQ(0, observer.tag_updated_counter());
734 EXPECT_EQ(0u, notification_log.size());
736 Watchers* const watchers = provided_file_system_->GetWatchers();
737 EXPECT_EQ(1u, watchers->size());
738 provided_file_system_->GetWatchers();
739 EXPECT_EQ("", watchers->begin()->second.last_tag);
743 // Notify about a change.
744 const storage::WatcherManager::ChangeType change_type =
745 storage::WatcherManager::CHANGED;
746 const std::string tag = "hello-world";
748 Log log;
749 provided_file_system_->Notify(
750 base::FilePath(kDirectoryPath), false /* recursive */, change_type,
751 make_scoped_ptr(new ProvidedFileSystemObserver::Changes), tag,
752 base::Bind(&LogStatus, base::Unretained(&log)));
753 base::RunLoop().RunUntilIdle();
755 // Confirm that the notification callback was called.
756 ASSERT_EQ(1u, notification_log.size());
757 EXPECT_EQ(change_type, notification_log[0]);
759 // Verify the observer event.
760 ASSERT_EQ(1u, observer.change_events().size());
761 const Observer::ChangeEvent* const change_event =
762 observer.change_events()[0];
763 EXPECT_EQ(change_type, change_event->change_type());
764 EXPECT_EQ(0u, change_event->changes().size());
766 // The tag should not be updated in advance, before all observers handle
767 // the notification.
768 Watchers* const watchers = provided_file_system_->GetWatchers();
769 EXPECT_EQ(1u, watchers->size());
770 provided_file_system_->GetWatchers();
771 EXPECT_EQ("", watchers->begin()->second.last_tag);
773 // Wait until all observers finish handling the notification.
774 observer.CompleteOnWatcherChanged();
775 base::RunLoop().RunUntilIdle();
777 ASSERT_EQ(1u, log.size());
778 EXPECT_EQ(base::File::FILE_OK, log[0]);
780 // Confirm, that the watcher still exists, and that the tag is updated.
781 ASSERT_EQ(1u, watchers->size());
782 EXPECT_EQ(tag, watchers->begin()->second.last_tag);
783 EXPECT_EQ(1, observer.list_changed_counter());
784 EXPECT_EQ(1, observer.tag_updated_counter());
788 // Notify about deleting of the watched entry.
789 const storage::WatcherManager::ChangeType change_type =
790 storage::WatcherManager::DELETED;
791 const ProvidedFileSystemObserver::Changes changes;
792 const std::string tag = "chocolate-disco";
794 Log log;
795 provided_file_system_->Notify(
796 base::FilePath(kDirectoryPath), false /* recursive */, change_type,
797 make_scoped_ptr(new ProvidedFileSystemObserver::Changes), tag,
798 base::Bind(&LogStatus, base::Unretained(&log)));
799 base::RunLoop().RunUntilIdle();
801 // Complete all change events.
802 observer.CompleteOnWatcherChanged();
803 base::RunLoop().RunUntilIdle();
805 ASSERT_EQ(1u, log.size());
806 EXPECT_EQ(base::File::FILE_OK, log[0]);
808 // Confirm that the notification callback was called.
809 ASSERT_EQ(2u, notification_log.size());
810 EXPECT_EQ(change_type, notification_log[1]);
812 // Verify the observer event.
813 ASSERT_EQ(2u, observer.change_events().size());
814 const Observer::ChangeEvent* const change_event =
815 observer.change_events()[1];
816 EXPECT_EQ(change_type, change_event->change_type());
817 EXPECT_EQ(0u, change_event->changes().size());
820 // Confirm, that the watcher is removed.
822 Watchers* const watchers = provided_file_system_->GetWatchers();
823 EXPECT_EQ(0u, watchers->size());
824 EXPECT_EQ(2, observer.list_changed_counter());
825 EXPECT_EQ(2, observer.tag_updated_counter());
828 provided_file_system_->RemoveObserver(&observer);
831 TEST_F(FileSystemProviderProvidedFileSystemTest, OpenedFiles) {
832 Observer observer;
833 provided_file_system_->AddObserver(&observer);
835 OpenFileLog log;
836 provided_file_system_->OpenFile(
837 base::FilePath(kFilePath), OPEN_FILE_MODE_WRITE,
838 base::Bind(LogOpenFile, base::Unretained(&log)));
839 base::RunLoop().RunUntilIdle();
841 ASSERT_EQ(1u, log.size());
842 EXPECT_EQ(base::File::FILE_OK, log[0].second);
843 const int file_handle = log[0].first;
845 const OpenedFiles& opened_files = provided_file_system_->GetOpenedFiles();
846 const auto opened_file_it = opened_files.find(file_handle);
847 ASSERT_NE(opened_files.end(), opened_file_it);
848 EXPECT_EQ(kFilePath, opened_file_it->second.file_path.AsUTF8Unsafe());
849 EXPECT_EQ(OPEN_FILE_MODE_WRITE, opened_file_it->second.mode);
851 Log close_log;
852 provided_file_system_->CloseFile(
853 file_handle, base::Bind(LogStatus, base::Unretained(&close_log)));
854 base::RunLoop().RunUntilIdle();
856 ASSERT_EQ(1u, close_log.size());
857 EXPECT_EQ(base::File::FILE_OK, close_log[0]);
858 EXPECT_EQ(0u, opened_files.size());
860 provided_file_system_->RemoveObserver(&observer);
863 TEST_F(FileSystemProviderProvidedFileSystemTest, OpenedFiles_OpeningFailure) {
864 Observer observer;
865 provided_file_system_->AddObserver(&observer);
867 event_router_->set_reply_result(base::File::FILE_ERROR_NOT_FOUND);
869 OpenFileLog log;
870 provided_file_system_->OpenFile(
871 base::FilePath(kFilePath), OPEN_FILE_MODE_WRITE,
872 base::Bind(LogOpenFile, base::Unretained(&log)));
873 base::RunLoop().RunUntilIdle();
875 ASSERT_EQ(1u, log.size());
876 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, log[0].second);
878 const OpenedFiles& opened_files = provided_file_system_->GetOpenedFiles();
879 EXPECT_EQ(0u, opened_files.size());
881 provided_file_system_->RemoveObserver(&observer);
884 TEST_F(FileSystemProviderProvidedFileSystemTest, OpenedFile_ClosingFailure) {
885 Observer observer;
886 provided_file_system_->AddObserver(&observer);
888 OpenFileLog log;
889 provided_file_system_->OpenFile(
890 base::FilePath(kFilePath), OPEN_FILE_MODE_WRITE,
891 base::Bind(LogOpenFile, base::Unretained(&log)));
892 base::RunLoop().RunUntilIdle();
894 ASSERT_EQ(1u, log.size());
895 EXPECT_EQ(base::File::FILE_OK, log[0].second);
896 const int file_handle = log[0].first;
898 const OpenedFiles& opened_files = provided_file_system_->GetOpenedFiles();
899 const auto opened_file_it = opened_files.find(file_handle);
900 ASSERT_NE(opened_files.end(), opened_file_it);
901 EXPECT_EQ(kFilePath, opened_file_it->second.file_path.AsUTF8Unsafe());
902 EXPECT_EQ(OPEN_FILE_MODE_WRITE, opened_file_it->second.mode);
904 // Simulate an error for closing a file. Still, the file should be closed
905 // in the C++ layer, anyway.
906 event_router_->set_reply_result(base::File::FILE_ERROR_NOT_FOUND);
908 Log close_log;
909 provided_file_system_->CloseFile(
910 file_handle, base::Bind(LogStatus, base::Unretained(&close_log)));
911 base::RunLoop().RunUntilIdle();
913 ASSERT_EQ(1u, close_log.size());
914 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, close_log[0]);
915 EXPECT_EQ(0u, opened_files.size());
917 provided_file_system_->RemoveObserver(&observer);
920 } // namespace file_system_provider
921 } // namespace chromeos