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"
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_internal.h"
26 #include "chrome/test/base/testing_profile.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "extensions/browser/event_router.h"
29 #include "storage/browser/fileapi/watcher_manager.h"
30 #include "testing/gtest/include/gtest/gtest.h"
33 namespace file_system_provider
{
36 const char kOrigin
[] =
37 "chrome-extension://abcabcabcabcabcabcabcabcabcabcabcabca/";
38 const char kAnotherOrigin
[] =
39 "chrome-extension://efgefgefgefgefgefgefgefgefgefgefgefge/";
40 const char kExtensionId
[] = "mbflcebpggnecokmikipoihdbecnjfoj";
41 const char kFileSystemId
[] = "camera-pictures";
42 const char kDisplayName
[] = "Camera Pictures";
43 const base::FilePath::CharType kDirectoryPath
[] = "/hello/world";
45 // Fake implementation of the event router, mocking out a real extension.
46 // Handles requests and replies with fake answers back to the file system via
47 // the request manager.
48 class FakeEventRouter
: public extensions::EventRouter
{
50 FakeEventRouter(Profile
* profile
, ProvidedFileSystemInterface
* file_system
)
51 : EventRouter(profile
, NULL
),
52 file_system_(file_system
),
53 reply_result_(base::File::FILE_OK
) {}
54 virtual ~FakeEventRouter() {}
56 // Handles an event which would normally be routed to an extension. Instead
57 // replies with a hard coded response.
58 virtual void DispatchEventToExtension(
59 const std::string
& extension_id
,
60 scoped_ptr
<extensions::Event
> event
) override
{
61 ASSERT_TRUE(file_system_
);
62 std::string file_system_id
;
63 const base::DictionaryValue
* dictionary_value
= NULL
;
64 ASSERT_TRUE(event
->event_args
->GetDictionary(0, &dictionary_value
));
65 EXPECT_TRUE(dictionary_value
->GetString("fileSystemId", &file_system_id
));
66 EXPECT_EQ(kFileSystemId
, file_system_id
);
68 EXPECT_TRUE(dictionary_value
->GetInteger("requestId", &request_id
));
69 EXPECT_TRUE(event
->event_name
== extensions::api::file_system_provider::
70 OnAddWatcherRequested::kEventName
||
71 event
->event_name
== extensions::api::file_system_provider::
72 OnRemoveWatcherRequested::kEventName
);
74 if (reply_result_
== base::File::FILE_OK
) {
75 base::ListValue value_as_list
;
76 value_as_list
.Set(0, new base::StringValue(kFileSystemId
));
77 value_as_list
.Set(1, new base::FundamentalValue(request_id
));
78 value_as_list
.Set(2, new base::FundamentalValue(0) /* execution_time */);
80 using extensions::api::file_system_provider_internal::
81 OperationRequestedSuccess::Params
;
82 scoped_ptr
<Params
> params(Params::Create(value_as_list
));
83 ASSERT_TRUE(params
.get());
84 file_system_
->GetRequestManager()->FulfillRequest(
86 RequestValue::CreateForOperationSuccess(params
.Pass()),
87 false /* has_more */);
89 file_system_
->GetRequestManager()->RejectRequest(
90 request_id
, make_scoped_ptr(new RequestValue()), reply_result_
);
94 void set_reply_result(base::File::Error result
) { reply_result_
= result
; }
97 ProvidedFileSystemInterface
* const file_system_
; // Not owned.
98 base::File::Error reply_result_
;
99 DISALLOW_COPY_AND_ASSIGN(FakeEventRouter
);
102 // Observes the tested file system.
103 class Observer
: public ProvidedFileSystemObserver
{
107 ChangeEvent(storage::WatcherManager::ChangeType change_type
,
108 const ProvidedFileSystemObserver::Changes
& changes
)
109 : change_type_(change_type
), changes_(changes
) {}
110 virtual ~ChangeEvent() {}
112 storage::WatcherManager::ChangeType
change_type() const {
115 const ProvidedFileSystemObserver::Changes
& changes() const {
120 const storage::WatcherManager::ChangeType change_type_
;
121 const ProvidedFileSystemObserver::Changes changes_
;
123 DISALLOW_COPY_AND_ASSIGN(ChangeEvent
);
126 Observer() : list_changed_counter_(0), tag_updated_counter_(0) {}
128 // ProvidedFileSystemInterfaceObserver overrides.
129 virtual void OnWatcherChanged(
130 const ProvidedFileSystemInfo
& file_system_info
,
131 const Watcher
& watcher
,
132 storage::WatcherManager::ChangeType change_type
,
133 const ProvidedFileSystemObserver::Changes
& changes
,
134 const base::Closure
& callback
) override
{
135 EXPECT_EQ(kFileSystemId
, file_system_info
.file_system_id());
136 change_events_
.push_back(new ChangeEvent(change_type
, changes
));
137 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, callback
);
140 virtual void OnWatcherTagUpdated(
141 const ProvidedFileSystemInfo
& file_system_info
,
142 const Watcher
& watcher
) override
{
143 EXPECT_EQ(kFileSystemId
, file_system_info
.file_system_id());
144 ++tag_updated_counter_
;
147 virtual void OnWatcherListChanged(
148 const ProvidedFileSystemInfo
& file_system_info
,
149 const Watchers
& watchers
) override
{
150 EXPECT_EQ(kFileSystemId
, file_system_info
.file_system_id());
151 ++list_changed_counter_
;
154 int list_changed_counter() const { return list_changed_counter_
; }
155 const ScopedVector
<ChangeEvent
>& change_events() const {
156 return change_events_
;
158 int tag_updated_counter() const { return tag_updated_counter_
; }
161 ScopedVector
<ChangeEvent
> change_events_
;
162 int list_changed_counter_
;
163 int tag_updated_counter_
;
165 DISALLOW_COPY_AND_ASSIGN(Observer
);
168 // Stub notification manager, which works in unit tests.
169 class StubNotificationManager
: public NotificationManagerInterface
{
171 StubNotificationManager() {}
172 virtual ~StubNotificationManager() {}
174 // NotificationManagerInterface overrides.
175 virtual void ShowUnresponsiveNotification(
177 const NotificationCallback
& callback
) override
{}
178 virtual void HideUnresponsiveNotification(int id
) override
{}
181 DISALLOW_COPY_AND_ASSIGN(StubNotificationManager
);
184 typedef std::vector
<base::File::Error
> Log
;
185 typedef std::vector
<storage::WatcherManager::ChangeType
> NotificationLog
;
187 // Writes a |result| to the |log| vector.
188 void LogStatus(Log
* log
, base::File::Error result
) {
189 log
->push_back(result
);
192 // Writes an |change_type| to the |notification_log| vector.
193 void LogNotification(NotificationLog
* notification_log
,
194 storage::WatcherManager::ChangeType change_type
) {
195 notification_log
->push_back(change_type
);
200 class FileSystemProviderProvidedFileSystemTest
: public testing::Test
{
202 FileSystemProviderProvidedFileSystemTest() {}
203 virtual ~FileSystemProviderProvidedFileSystemTest() {}
205 virtual void SetUp() override
{
206 profile_
.reset(new TestingProfile
);
207 const base::FilePath mount_path
=
208 util::GetMountPath(profile_
.get(), kExtensionId
, kFileSystemId
);
209 MountOptions mount_options
;
210 mount_options
.file_system_id
= kFileSystemId
;
211 mount_options
.display_name
= kDisplayName
;
212 mount_options
.supports_notify_tag
= true;
213 file_system_info_
.reset(
214 new ProvidedFileSystemInfo(kExtensionId
, mount_options
, mount_path
));
215 provided_file_system_
.reset(
216 new ProvidedFileSystem(profile_
.get(), *file_system_info_
.get()));
218 new FakeEventRouter(profile_
.get(), provided_file_system_
.get()));
219 event_router_
->AddEventListener(extensions::api::file_system_provider::
220 OnAddWatcherRequested::kEventName
,
223 event_router_
->AddEventListener(extensions::api::file_system_provider::
224 OnRemoveWatcherRequested::kEventName
,
227 provided_file_system_
->SetEventRouterForTesting(event_router_
.get());
228 provided_file_system_
->SetNotificationManagerForTesting(
229 make_scoped_ptr(new StubNotificationManager
));
232 content::TestBrowserThreadBundle thread_bundle_
;
233 scoped_ptr
<TestingProfile
> profile_
;
234 scoped_ptr
<FakeEventRouter
> event_router_
;
235 scoped_ptr
<ProvidedFileSystemInfo
> file_system_info_
;
236 scoped_ptr
<ProvidedFileSystem
> provided_file_system_
;
239 TEST_F(FileSystemProviderProvidedFileSystemTest
, AutoUpdater
) {
241 base::Closure firstCallback
;
242 base::Closure secondCallback
;
245 // Auto updater is ref counted, and bound to all callbacks.
246 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
247 base::Bind(&LogStatus
, base::Unretained(&log
), base::File::FILE_OK
)));
249 firstCallback
= auto_updater
->CreateCallback();
250 secondCallback
= auto_updater
->CreateCallback();
253 // Getting out of scope, should not invoke updating if there are pending
255 EXPECT_EQ(0u, log
.size());
257 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, firstCallback
);
258 base::RunLoop().RunUntilIdle();
259 EXPECT_EQ(0u, log
.size());
261 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, secondCallback
);
262 base::RunLoop().RunUntilIdle();
263 EXPECT_EQ(1u, log
.size());
266 TEST_F(FileSystemProviderProvidedFileSystemTest
, AutoUpdater_NoCallbacks
) {
269 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
270 base::Bind(&LogStatus
, base::Unretained(&log
), base::File::FILE_OK
)));
272 EXPECT_EQ(1u, log
.size());
275 TEST_F(FileSystemProviderProvidedFileSystemTest
, AutoUpdater_CallbackIgnored
) {
278 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
279 base::Bind(&LogStatus
, base::Unretained(&log
), base::File::FILE_OK
)));
280 base::Closure callback
= auto_updater
->CreateCallback();
281 // The callback gets out of scope, so the ref counted auto updater instance
282 // gets deleted. Still, updating shouldn't be invoked, since the callback
285 base::RunLoop().RunUntilIdle();
286 EXPECT_EQ(0u, log
.size());
289 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_NotFound
) {
291 NotificationLog notification_log
;
294 provided_file_system_
->AddObserver(&observer
);
296 // First, set the extension response to an error.
297 event_router_
->set_reply_result(base::File::FILE_ERROR_NOT_FOUND
);
299 provided_file_system_
->AddWatcher(
301 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
302 false /* recursive */,
303 false /* persistent */,
304 base::Bind(&LogStatus
, base::Unretained(&log
)),
305 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
306 base::RunLoop().RunUntilIdle();
308 // The directory should not become watched because of an error.
309 ASSERT_EQ(1u, log
.size());
310 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, log
[0]);
311 EXPECT_EQ(0u, notification_log
.size());
313 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
314 EXPECT_EQ(0u, watchers
->size());
316 // The observer should not be called.
317 EXPECT_EQ(0, observer
.list_changed_counter());
318 EXPECT_EQ(0, observer
.tag_updated_counter());
320 provided_file_system_
->RemoveObserver(&observer
);
323 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher
) {
327 provided_file_system_
->AddObserver(&observer
);
329 provided_file_system_
->AddWatcher(
331 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
332 false /* recursive */,
333 true /* persistent */,
334 base::Bind(&LogStatus
, base::Unretained(&log
)),
335 storage::WatcherManager::NotificationCallback());
336 base::RunLoop().RunUntilIdle();
338 ASSERT_EQ(1u, log
.size());
339 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
340 EXPECT_EQ(1, observer
.list_changed_counter());
341 EXPECT_EQ(0, observer
.tag_updated_counter());
343 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
344 ASSERT_EQ(1u, watchers
->size());
345 const Watcher
& watcher
= watchers
->begin()->second
;
346 EXPECT_EQ(FILE_PATH_LITERAL(kDirectoryPath
), watcher
.entry_path
.value());
347 EXPECT_FALSE(watcher
.recursive
);
348 EXPECT_EQ("", watcher
.last_tag
);
350 provided_file_system_
->RemoveObserver(&observer
);
353 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_PersistentIllegal
) {
355 // Adding a persistent watcher with a notification callback is not allowed,
356 // as it's basically impossible to restore the callback after a shutdown.
358 NotificationLog notification_log
;
361 provided_file_system_
->AddObserver(&observer
);
363 provided_file_system_
->AddWatcher(
365 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
366 false /* recursive */,
367 true /* persistent */,
368 base::Bind(&LogStatus
, base::Unretained(&log
)),
369 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
370 base::RunLoop().RunUntilIdle();
372 ASSERT_EQ(1u, log
.size());
373 EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION
, log
[0]);
374 EXPECT_EQ(0, observer
.list_changed_counter());
375 EXPECT_EQ(0, observer
.tag_updated_counter());
377 provided_file_system_
->RemoveObserver(&observer
);
381 // Adding a persistent watcher is not allowed if the file system doesn't
382 // support the notify tag. It's because the notify tag is essential to be
383 // able to recreate notification during shutdown.
387 // Create a provided file system interface, which does not support a notify
389 const base::FilePath mount_path
=
390 util::GetMountPath(profile_
.get(), kExtensionId
, kFileSystemId
);
391 MountOptions mount_options
;
392 mount_options
.file_system_id
= kFileSystemId
;
393 mount_options
.display_name
= kDisplayName
;
394 mount_options
.supports_notify_tag
= false;
395 ProvidedFileSystemInfo
file_system_info(
396 kExtensionId
, mount_options
, mount_path
);
397 ProvidedFileSystem
simple_provided_file_system(profile_
.get(),
399 simple_provided_file_system
.SetEventRouterForTesting(event_router_
.get());
400 simple_provided_file_system
.SetNotificationManagerForTesting(
401 make_scoped_ptr(new StubNotificationManager
));
403 simple_provided_file_system
.AddObserver(&observer
);
405 simple_provided_file_system
.AddWatcher(
407 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
408 false /* recursive */,
409 true /* persistent */,
410 base::Bind(&LogStatus
, base::Unretained(&log
)),
411 storage::WatcherManager::NotificationCallback());
412 base::RunLoop().RunUntilIdle();
414 ASSERT_EQ(1u, log
.size());
415 EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION
, log
[0]);
416 EXPECT_EQ(0, observer
.list_changed_counter());
417 EXPECT_EQ(0, observer
.tag_updated_counter());
419 simple_provided_file_system
.RemoveObserver(&observer
);
423 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_Exists
) {
425 provided_file_system_
->AddObserver(&observer
);
428 // First watch a directory not recursively.
430 provided_file_system_
->AddWatcher(
432 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
433 false /* recursive */,
434 true /* persistent */,
435 base::Bind(&LogStatus
, base::Unretained(&log
)),
436 storage::WatcherManager::NotificationCallback());
437 base::RunLoop().RunUntilIdle();
439 ASSERT_EQ(1u, log
.size());
440 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
441 EXPECT_EQ(1, observer
.list_changed_counter());
442 EXPECT_EQ(0, observer
.tag_updated_counter());
444 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
445 ASSERT_TRUE(watchers
);
446 ASSERT_EQ(1u, watchers
->size());
447 const auto& watcher_it
= watchers
->find(
448 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
449 false /* recursive */));
450 ASSERT_NE(watchers
->end(), watcher_it
);
452 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
453 const auto& subscriber_it
=
454 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
455 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
456 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
457 EXPECT_TRUE(subscriber_it
->second
.persistent
);
461 // Create another non-recursive observer. That should fail.
463 provided_file_system_
->AddWatcher(
465 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
466 false /* recursive */,
467 true /* persistent */,
468 base::Bind(&LogStatus
, base::Unretained(&log
)),
469 storage::WatcherManager::NotificationCallback());
470 base::RunLoop().RunUntilIdle();
472 ASSERT_EQ(1u, log
.size());
473 EXPECT_EQ(base::File::FILE_ERROR_EXISTS
, log
[0]);
474 EXPECT_EQ(1, observer
.list_changed_counter()); // No changes on the list.
475 EXPECT_EQ(0, observer
.tag_updated_counter());
479 // Lastly, create another recursive observer. That should succeed.
481 provided_file_system_
->AddWatcher(
483 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
484 true /* recursive */,
485 true /* persistent */,
486 base::Bind(&LogStatus
, base::Unretained(&log
)),
487 storage::WatcherManager::NotificationCallback());
488 base::RunLoop().RunUntilIdle();
490 ASSERT_EQ(1u, log
.size());
491 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
492 EXPECT_EQ(2, observer
.list_changed_counter());
493 EXPECT_EQ(0, observer
.tag_updated_counter());
496 provided_file_system_
->RemoveObserver(&observer
);
499 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_MultipleOrigins
) {
501 provided_file_system_
->AddObserver(&observer
);
504 // First watch a directory not recursively.
506 NotificationLog notification_log
;
508 provided_file_system_
->AddWatcher(
510 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
511 false /* recursive */,
512 false /* persistent */,
513 base::Bind(&LogStatus
, base::Unretained(&log
)),
514 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
515 base::RunLoop().RunUntilIdle();
517 ASSERT_EQ(1u, log
.size());
518 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
519 EXPECT_EQ(1, observer
.list_changed_counter());
520 EXPECT_EQ(0, observer
.tag_updated_counter());
521 EXPECT_EQ(0u, notification_log
.size());
523 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
524 ASSERT_TRUE(watchers
);
525 ASSERT_EQ(1u, watchers
->size());
526 const auto& watcher_it
= watchers
->find(
527 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
528 false /* recursive */));
529 ASSERT_NE(watchers
->end(), watcher_it
);
531 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
532 const auto& subscriber_it
=
533 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
534 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
535 EXPECT_EQ(kOrigin
, subscriber_it
->first
.spec());
536 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
537 EXPECT_FALSE(subscriber_it
->second
.persistent
);
541 // Create another watcher, but recursive and with a different origin.
543 NotificationLog notification_log
;
545 provided_file_system_
->AddWatcher(
546 GURL(kAnotherOrigin
),
547 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
548 true /* recursive */,
549 false /* persistent */,
550 base::Bind(&LogStatus
, base::Unretained(&log
)),
551 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
552 base::RunLoop().RunUntilIdle();
554 ASSERT_EQ(1u, log
.size());
555 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
556 EXPECT_EQ(2, observer
.list_changed_counter());
557 EXPECT_EQ(0, observer
.tag_updated_counter());
558 EXPECT_EQ(0u, notification_log
.size());
560 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
561 ASSERT_TRUE(watchers
);
562 ASSERT_EQ(2u, watchers
->size());
563 const auto& watcher_it
= watchers
->find(
564 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
565 false /* recursive */));
566 ASSERT_NE(watchers
->end(), watcher_it
);
568 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
569 const auto& subscriber_it
=
570 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
571 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
572 EXPECT_EQ(kOrigin
, subscriber_it
->first
.spec());
573 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
574 EXPECT_FALSE(subscriber_it
->second
.persistent
);
578 // Remove the second watcher gracefully.
580 provided_file_system_
->RemoveWatcher(
581 GURL(kAnotherOrigin
),
582 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
583 true /* recursive */,
584 base::Bind(&LogStatus
, base::Unretained(&log
)));
585 base::RunLoop().RunUntilIdle();
587 ASSERT_EQ(1u, log
.size());
588 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
589 EXPECT_EQ(3, observer
.list_changed_counter());
590 EXPECT_EQ(0, observer
.tag_updated_counter());
592 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
593 ASSERT_TRUE(watchers
);
594 EXPECT_EQ(1u, watchers
->size());
595 const auto& watcher_it
= watchers
->find(
596 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
597 false /* recursive */));
598 ASSERT_NE(watchers
->end(), watcher_it
);
600 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
601 const auto& subscriber_it
=
602 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
603 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
604 EXPECT_EQ(kOrigin
, subscriber_it
->first
.spec());
605 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
606 EXPECT_FALSE(subscriber_it
->second
.persistent
);
609 provided_file_system_
->RemoveObserver(&observer
);
612 TEST_F(FileSystemProviderProvidedFileSystemTest
, RemoveWatcher
) {
614 provided_file_system_
->AddObserver(&observer
);
617 // First, confirm that removing a watcher which does not exist results in an
620 provided_file_system_
->RemoveWatcher(
622 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
623 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.
636 NotificationLog notification_log
;
638 provided_file_system_
->AddWatcher(
640 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
641 false /* recursive */,
642 false /* persistent */,
643 base::Bind(&LogStatus
, base::Unretained(&log
)),
644 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
645 base::RunLoop().RunUntilIdle();
647 ASSERT_EQ(1u, log
.size());
648 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
649 EXPECT_EQ(1, observer
.list_changed_counter());
650 EXPECT_EQ(0, observer
.tag_updated_counter());
651 EXPECT_EQ(0u, notification_log
.size());
653 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
654 EXPECT_EQ(1u, watchers
->size());
658 // Remove a watcher gracefully.
660 provided_file_system_
->RemoveWatcher(
662 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
663 false /* recursive */,
664 base::Bind(&LogStatus
, base::Unretained(&log
)));
665 base::RunLoop().RunUntilIdle();
667 ASSERT_EQ(1u, log
.size());
668 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
669 EXPECT_EQ(2, observer
.list_changed_counter());
670 EXPECT_EQ(0, observer
.tag_updated_counter());
672 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
673 EXPECT_EQ(0u, watchers
->size());
677 // Confirm that it's possible to watch it again.
679 NotificationLog notification_log
;
681 provided_file_system_
->AddWatcher(
683 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
684 false /* recursive */,
685 false /* persistent */,
686 base::Bind(&LogStatus
, base::Unretained(&log
)),
687 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
688 base::RunLoop().RunUntilIdle();
690 ASSERT_EQ(1u, log
.size());
691 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
692 EXPECT_EQ(3, observer
.list_changed_counter());
693 EXPECT_EQ(0, observer
.tag_updated_counter());
694 EXPECT_EQ(0u, notification_log
.size());
696 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
697 EXPECT_EQ(1u, watchers
->size());
701 // Finally, remove it, but with an error from extension. That should result
702 // in a removed watcher, anyway. The error code should not be passed.
703 event_router_
->set_reply_result(base::File::FILE_ERROR_FAILED
);
706 provided_file_system_
->RemoveWatcher(
708 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
709 false /* recursive */,
710 base::Bind(&LogStatus
, base::Unretained(&log
)));
711 base::RunLoop().RunUntilIdle();
713 ASSERT_EQ(1u, log
.size());
714 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
715 EXPECT_EQ(4, observer
.list_changed_counter());
716 EXPECT_EQ(0, observer
.tag_updated_counter());
718 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
719 EXPECT_EQ(0u, watchers
->size());
722 provided_file_system_
->RemoveObserver(&observer
);
725 TEST_F(FileSystemProviderProvidedFileSystemTest
, Notify
) {
727 provided_file_system_
->AddObserver(&observer
);
728 NotificationLog notification_log
;
731 // Watch a directory.
734 provided_file_system_
->AddWatcher(
736 base::FilePath::FromUTF8Unsafe(kDirectoryPath
),
737 false /* recursive */,
738 false /* persistent */,
739 base::Bind(&LogStatus
, base::Unretained(&log
)),
740 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
741 base::RunLoop().RunUntilIdle();
743 ASSERT_EQ(1u, log
.size());
744 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
745 EXPECT_EQ(1, observer
.list_changed_counter());
746 EXPECT_EQ(0, observer
.tag_updated_counter());
747 EXPECT_EQ(0u, notification_log
.size());
749 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
750 EXPECT_EQ(1u, watchers
->size());
751 provided_file_system_
->GetWatchers();
752 EXPECT_EQ("", watchers
->begin()->second
.last_tag
);
756 // Notify about a change.
757 const storage::WatcherManager::ChangeType change_type
=
758 storage::WatcherManager::CHANGED
;
759 const std::string tag
= "hello-world";
762 provided_file_system_
->Notify(
763 base::FilePath::FromUTF8Unsafe(kDirectoryPath
), false /* recursive */,
764 change_type
, make_scoped_ptr(new ProvidedFileSystemObserver::Changes
),
765 tag
, base::Bind(&LogStatus
, base::Unretained(&log
)));
767 // Confirm that the notification callback was called.
768 ASSERT_EQ(1u, notification_log
.size());
769 EXPECT_EQ(change_type
, notification_log
[0]);
771 // Verify the observer event.
772 ASSERT_EQ(1u, observer
.change_events().size());
773 const Observer::ChangeEvent
* const change_event
=
774 observer
.change_events()[0];
775 EXPECT_EQ(change_type
, change_event
->change_type());
776 EXPECT_EQ(0u, change_event
->changes().size());
778 // The tag should not be updated in advance, before all observers handle
780 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
781 EXPECT_EQ(1u, watchers
->size());
782 provided_file_system_
->GetWatchers();
783 EXPECT_EQ("", watchers
->begin()->second
.last_tag
);
785 // Wait until all observers finish handling the notification.
786 base::RunLoop().RunUntilIdle();
788 ASSERT_EQ(1u, log
.size());
789 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
791 // Confirm, that the watcher still exists, and that the tag is updated.
792 ASSERT_EQ(1u, watchers
->size());
793 EXPECT_EQ(tag
, watchers
->begin()->second
.last_tag
);
794 EXPECT_EQ(1, observer
.list_changed_counter());
795 EXPECT_EQ(1, observer
.tag_updated_counter());
799 // Notify about deleting of the watched entry.
800 const storage::WatcherManager::ChangeType change_type
=
801 storage::WatcherManager::DELETED
;
802 const ProvidedFileSystemObserver::Changes changes
;
803 const std::string tag
= "chocolate-disco";
806 provided_file_system_
->Notify(
807 base::FilePath::FromUTF8Unsafe(kDirectoryPath
), false /* recursive */,
808 change_type
, make_scoped_ptr(new ProvidedFileSystemObserver::Changes
),
809 tag
, base::Bind(&LogStatus
, base::Unretained(&log
)));
810 base::RunLoop().RunUntilIdle();
811 ASSERT_EQ(1u, log
.size());
812 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
814 // Confirm that the notification callback was called.
815 ASSERT_EQ(2u, notification_log
.size());
816 EXPECT_EQ(change_type
, notification_log
[1]);
818 // Verify the observer event.
819 ASSERT_EQ(2u, observer
.change_events().size());
820 const Observer::ChangeEvent
* const change_event
=
821 observer
.change_events()[1];
822 EXPECT_EQ(change_type
, change_event
->change_type());
823 EXPECT_EQ(0u, change_event
->changes().size());
826 // Confirm, that the watcher is removed.
828 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
829 EXPECT_EQ(0u, watchers
->size());
830 EXPECT_EQ(2, observer
.list_changed_counter());
831 EXPECT_EQ(2, observer
.tag_updated_counter());
834 provided_file_system_
->RemoveObserver(&observer
);
837 } // namespace file_system_provider
838 } // namespace chromeos