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
[] =
44 FILE_PATH_LITERAL("/hello/world");
45 const base::FilePath::CharType kFilePath
[] =
46 FILE_PATH_LITERAL("/welcome/to/my/world");
48 // Fake implementation of the event router, mocking out a real extension.
49 // Handles requests and replies with fake answers back to the file system via
50 // the request manager.
51 class FakeEventRouter
: public extensions::EventRouter
{
53 FakeEventRouter(Profile
* profile
, ProvidedFileSystemInterface
* file_system
)
54 : EventRouter(profile
, NULL
),
55 file_system_(file_system
),
56 reply_result_(base::File::FILE_OK
) {}
57 ~FakeEventRouter() override
{}
59 // Handles an event which would normally be routed to an extension. Instead
60 // replies with a hard coded response.
61 void DispatchEventToExtension(const std::string
& extension_id
,
62 scoped_ptr
<extensions::Event
> event
) override
{
63 ASSERT_TRUE(file_system_
);
64 std::string file_system_id
;
65 const base::DictionaryValue
* dictionary_value
= NULL
;
66 ASSERT_TRUE(event
->event_args
->GetDictionary(0, &dictionary_value
));
67 EXPECT_TRUE(dictionary_value
->GetString("fileSystemId", &file_system_id
));
68 EXPECT_EQ(kFileSystemId
, file_system_id
);
70 EXPECT_TRUE(dictionary_value
->GetInteger("requestId", &request_id
));
71 EXPECT_TRUE(event
->event_name
== extensions::api::file_system_provider::
72 OnAddWatcherRequested::kEventName
||
73 event
->event_name
== extensions::api::file_system_provider::
74 OnRemoveWatcherRequested::kEventName
||
75 event
->event_name
== extensions::api::file_system_provider::
76 OnOpenFileRequested::kEventName
||
77 event
->event_name
== extensions::api::file_system_provider::
78 OnCloseFileRequested::kEventName
);
80 if (reply_result_
== base::File::FILE_OK
) {
81 base::ListValue value_as_list
;
82 value_as_list
.Set(0, new base::StringValue(kFileSystemId
));
83 value_as_list
.Set(1, new base::FundamentalValue(request_id
));
84 value_as_list
.Set(2, new base::FundamentalValue(0) /* execution_time */);
86 using extensions::api::file_system_provider_internal::
87 OperationRequestedSuccess::Params
;
88 scoped_ptr
<Params
> params(Params::Create(value_as_list
));
89 ASSERT_TRUE(params
.get());
90 file_system_
->GetRequestManager()->FulfillRequest(
92 RequestValue::CreateForOperationSuccess(params
.Pass()),
93 false /* has_more */);
95 file_system_
->GetRequestManager()->RejectRequest(
96 request_id
, make_scoped_ptr(new RequestValue()), reply_result_
);
100 void set_reply_result(base::File::Error result
) { reply_result_
= result
; }
103 ProvidedFileSystemInterface
* const file_system_
; // Not owned.
104 base::File::Error reply_result_
;
105 DISALLOW_COPY_AND_ASSIGN(FakeEventRouter
);
108 // Observes the tested file system.
109 class Observer
: public ProvidedFileSystemObserver
{
113 ChangeEvent(storage::WatcherManager::ChangeType change_type
,
114 const ProvidedFileSystemObserver::Changes
& changes
)
115 : change_type_(change_type
), changes_(changes
) {}
116 virtual ~ChangeEvent() {}
118 storage::WatcherManager::ChangeType
change_type() const {
121 const ProvidedFileSystemObserver::Changes
& changes() const {
126 const storage::WatcherManager::ChangeType change_type_
;
127 const ProvidedFileSystemObserver::Changes changes_
;
129 DISALLOW_COPY_AND_ASSIGN(ChangeEvent
);
132 Observer() : list_changed_counter_(0), tag_updated_counter_(0) {}
134 // ProvidedFileSystemInterfaceObserver overrides.
135 void OnWatcherChanged(const ProvidedFileSystemInfo
& file_system_info
,
136 const Watcher
& watcher
,
137 storage::WatcherManager::ChangeType change_type
,
138 const ProvidedFileSystemObserver::Changes
& changes
,
139 const base::Closure
& callback
) override
{
140 EXPECT_EQ(kFileSystemId
, file_system_info
.file_system_id());
141 change_events_
.push_back(new ChangeEvent(change_type
, changes
));
142 complete_callback_
= callback
;
145 void OnWatcherTagUpdated(const ProvidedFileSystemInfo
& file_system_info
,
146 const Watcher
& watcher
) override
{
147 EXPECT_EQ(kFileSystemId
, file_system_info
.file_system_id());
148 ++tag_updated_counter_
;
151 void OnWatcherListChanged(const ProvidedFileSystemInfo
& file_system_info
,
152 const Watchers
& watchers
) override
{
153 EXPECT_EQ(kFileSystemId
, file_system_info
.file_system_id());
154 ++list_changed_counter_
;
157 // Completes handling the OnWatcherChanged event.
158 void CompleteOnWatcherChanged() {
159 DCHECK(!complete_callback_
.is_null());
160 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
162 complete_callback_
= base::Closure();
165 int list_changed_counter() const { return list_changed_counter_
; }
166 const ScopedVector
<ChangeEvent
>& change_events() const {
167 return change_events_
;
169 int tag_updated_counter() const { return tag_updated_counter_
; }
172 ScopedVector
<ChangeEvent
> change_events_
;
173 int list_changed_counter_
;
174 int tag_updated_counter_
;
175 base::Closure complete_callback_
;
177 DISALLOW_COPY_AND_ASSIGN(Observer
);
180 // Stub notification manager, which works in unit tests.
181 class StubNotificationManager
: public NotificationManagerInterface
{
183 StubNotificationManager() {}
184 ~StubNotificationManager() override
{}
186 // NotificationManagerInterface overrides.
187 void ShowUnresponsiveNotification(
189 const NotificationCallback
& callback
) override
{}
190 void HideUnresponsiveNotification(int id
) override
{}
193 DISALLOW_COPY_AND_ASSIGN(StubNotificationManager
);
196 typedef std::vector
<base::File::Error
> Log
;
197 typedef std::vector
<storage::WatcherManager::ChangeType
> NotificationLog
;
198 typedef std::vector
<std::pair
<int, base::File::Error
>> OpenFileLog
;
200 // Writes a |result| to the |log| vector.
201 void LogStatus(Log
* log
, base::File::Error result
) {
202 log
->push_back(result
);
205 // Writes a |change_type| to the |notification_log| vector.
206 void LogNotification(NotificationLog
* notification_log
,
207 storage::WatcherManager::ChangeType change_type
) {
208 notification_log
->push_back(change_type
);
211 // Writes a |file_handle| and |result| to the |open_file_log| vector.
212 void LogOpenFile(OpenFileLog
* open_file_log
,
214 base::File::Error result
) {
215 open_file_log
->push_back(std::make_pair(file_handle
, result
));
220 class FileSystemProviderProvidedFileSystemTest
: public testing::Test
{
222 FileSystemProviderProvidedFileSystemTest() {}
223 ~FileSystemProviderProvidedFileSystemTest() override
{}
225 void SetUp() override
{
226 profile_
.reset(new TestingProfile
);
227 const base::FilePath mount_path
=
228 util::GetMountPath(profile_
.get(), kExtensionId
, kFileSystemId
);
229 MountOptions mount_options
;
230 mount_options
.file_system_id
= kFileSystemId
;
231 mount_options
.display_name
= kDisplayName
;
232 mount_options
.supports_notify_tag
= true;
233 mount_options
.writable
= true;
234 file_system_info_
.reset(
235 new ProvidedFileSystemInfo(kExtensionId
, mount_options
, mount_path
));
236 provided_file_system_
.reset(
237 new ProvidedFileSystem(profile_
.get(), *file_system_info_
.get()));
239 new FakeEventRouter(profile_
.get(), provided_file_system_
.get()));
240 event_router_
->AddEventListener(extensions::api::file_system_provider::
241 OnAddWatcherRequested::kEventName
,
244 event_router_
->AddEventListener(extensions::api::file_system_provider::
245 OnRemoveWatcherRequested::kEventName
,
248 event_router_
->AddEventListener(
249 extensions::api::file_system_provider::OnOpenFileRequested::kEventName
,
251 event_router_
->AddEventListener(
252 extensions::api::file_system_provider::OnCloseFileRequested::kEventName
,
254 provided_file_system_
->SetEventRouterForTesting(event_router_
.get());
255 provided_file_system_
->SetNotificationManagerForTesting(
256 make_scoped_ptr(new StubNotificationManager
));
259 content::TestBrowserThreadBundle thread_bundle_
;
260 scoped_ptr
<TestingProfile
> profile_
;
261 scoped_ptr
<FakeEventRouter
> event_router_
;
262 scoped_ptr
<ProvidedFileSystemInfo
> file_system_info_
;
263 scoped_ptr
<ProvidedFileSystem
> provided_file_system_
;
266 TEST_F(FileSystemProviderProvidedFileSystemTest
, AutoUpdater
) {
268 base::Closure firstCallback
;
269 base::Closure secondCallback
;
272 // Auto updater is ref counted, and bound to all callbacks.
273 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
274 base::Bind(&LogStatus
, base::Unretained(&log
), base::File::FILE_OK
)));
276 firstCallback
= auto_updater
->CreateCallback();
277 secondCallback
= auto_updater
->CreateCallback();
280 // Getting out of scope, should not invoke updating if there are pending
282 EXPECT_EQ(0u, log
.size());
284 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, firstCallback
);
285 base::RunLoop().RunUntilIdle();
286 EXPECT_EQ(0u, log
.size());
288 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
, secondCallback
);
289 base::RunLoop().RunUntilIdle();
290 EXPECT_EQ(1u, log
.size());
293 TEST_F(FileSystemProviderProvidedFileSystemTest
, AutoUpdater_NoCallbacks
) {
296 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
297 base::Bind(&LogStatus
, base::Unretained(&log
), base::File::FILE_OK
)));
299 EXPECT_EQ(1u, log
.size());
302 TEST_F(FileSystemProviderProvidedFileSystemTest
, AutoUpdater_CallbackIgnored
) {
305 scoped_refptr
<AutoUpdater
> auto_updater(new AutoUpdater(
306 base::Bind(&LogStatus
, base::Unretained(&log
), base::File::FILE_OK
)));
307 base::Closure callback
= auto_updater
->CreateCallback();
308 // The callback gets out of scope, so the ref counted auto updater instance
309 // gets deleted. Still, updating shouldn't be invoked, since the callback
312 base::RunLoop().RunUntilIdle();
313 EXPECT_EQ(0u, log
.size());
316 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_NotFound
) {
318 NotificationLog notification_log
;
321 provided_file_system_
->AddObserver(&observer
);
323 // First, set the extension response to an error.
324 event_router_
->set_reply_result(base::File::FILE_ERROR_NOT_FOUND
);
326 provided_file_system_
->AddWatcher(
327 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
328 false /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
329 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
330 base::RunLoop().RunUntilIdle();
332 // The directory should not become watched because of an error.
333 ASSERT_EQ(1u, log
.size());
334 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, log
[0]);
335 EXPECT_EQ(0u, notification_log
.size());
337 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
338 EXPECT_EQ(0u, watchers
->size());
340 // The observer should not be called.
341 EXPECT_EQ(0, observer
.list_changed_counter());
342 EXPECT_EQ(0, observer
.tag_updated_counter());
344 provided_file_system_
->RemoveObserver(&observer
);
347 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher
) {
351 provided_file_system_
->AddObserver(&observer
);
353 provided_file_system_
->AddWatcher(
354 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
355 true /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
356 storage::WatcherManager::NotificationCallback());
357 base::RunLoop().RunUntilIdle();
359 ASSERT_EQ(1u, log
.size());
360 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
361 EXPECT_EQ(1, observer
.list_changed_counter());
362 EXPECT_EQ(0, observer
.tag_updated_counter());
364 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
365 ASSERT_EQ(1u, watchers
->size());
366 const Watcher
& watcher
= watchers
->begin()->second
;
367 EXPECT_EQ(FILE_PATH_LITERAL(kDirectoryPath
), watcher
.entry_path
.value());
368 EXPECT_FALSE(watcher
.recursive
);
369 EXPECT_EQ("", watcher
.last_tag
);
371 provided_file_system_
->RemoveObserver(&observer
);
374 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_PersistentIllegal
) {
376 // Adding a persistent watcher with a notification callback is not allowed,
377 // as it's basically impossible to restore the callback after a shutdown.
379 NotificationLog notification_log
;
382 provided_file_system_
->AddObserver(&observer
);
384 provided_file_system_
->AddWatcher(
385 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
386 true /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
387 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
388 base::RunLoop().RunUntilIdle();
390 ASSERT_EQ(1u, log
.size());
391 EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION
, log
[0]);
392 EXPECT_EQ(0, observer
.list_changed_counter());
393 EXPECT_EQ(0, observer
.tag_updated_counter());
395 provided_file_system_
->RemoveObserver(&observer
);
399 // Adding a persistent watcher is not allowed if the file system doesn't
400 // support the notify tag. It's because the notify tag is essential to be
401 // able to recreate notification during shutdown.
405 // Create a provided file system interface, which does not support a notify
407 const base::FilePath mount_path
=
408 util::GetMountPath(profile_
.get(), kExtensionId
, kFileSystemId
);
409 MountOptions mount_options
;
410 mount_options
.file_system_id
= kFileSystemId
;
411 mount_options
.display_name
= kDisplayName
;
412 mount_options
.supports_notify_tag
= false;
413 ProvidedFileSystemInfo
file_system_info(
414 kExtensionId
, mount_options
, mount_path
);
415 ProvidedFileSystem
simple_provided_file_system(profile_
.get(),
417 simple_provided_file_system
.SetEventRouterForTesting(event_router_
.get());
418 simple_provided_file_system
.SetNotificationManagerForTesting(
419 make_scoped_ptr(new StubNotificationManager
));
421 simple_provided_file_system
.AddObserver(&observer
);
423 simple_provided_file_system
.AddWatcher(
424 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
425 true /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
426 storage::WatcherManager::NotificationCallback());
427 base::RunLoop().RunUntilIdle();
429 ASSERT_EQ(1u, log
.size());
430 EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION
, log
[0]);
431 EXPECT_EQ(0, observer
.list_changed_counter());
432 EXPECT_EQ(0, observer
.tag_updated_counter());
434 simple_provided_file_system
.RemoveObserver(&observer
);
438 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_Exists
) {
440 provided_file_system_
->AddObserver(&observer
);
443 // First watch a directory not recursively.
445 provided_file_system_
->AddWatcher(
446 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
447 true /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
448 storage::WatcherManager::NotificationCallback());
449 base::RunLoop().RunUntilIdle();
451 ASSERT_EQ(1u, log
.size());
452 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
453 EXPECT_EQ(1, observer
.list_changed_counter());
454 EXPECT_EQ(0, observer
.tag_updated_counter());
456 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
457 ASSERT_TRUE(watchers
);
458 ASSERT_EQ(1u, watchers
->size());
459 const auto& watcher_it
= watchers
->find(
460 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
461 false /* recursive */));
462 ASSERT_NE(watchers
->end(), watcher_it
);
464 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
465 const auto& subscriber_it
=
466 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
467 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
468 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
469 EXPECT_TRUE(subscriber_it
->second
.persistent
);
473 // Create another non-recursive observer. That should fail.
475 provided_file_system_
->AddWatcher(
476 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
477 true /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
478 storage::WatcherManager::NotificationCallback());
479 base::RunLoop().RunUntilIdle();
481 ASSERT_EQ(1u, log
.size());
482 EXPECT_EQ(base::File::FILE_ERROR_EXISTS
, log
[0]);
483 EXPECT_EQ(1, observer
.list_changed_counter()); // No changes on the list.
484 EXPECT_EQ(0, observer
.tag_updated_counter());
488 // Lastly, create another recursive observer. That should succeed.
490 provided_file_system_
->AddWatcher(
491 GURL(kOrigin
), base::FilePath(kDirectoryPath
), true /* recursive */,
492 true /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
493 storage::WatcherManager::NotificationCallback());
494 base::RunLoop().RunUntilIdle();
496 ASSERT_EQ(1u, log
.size());
497 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
498 EXPECT_EQ(2, observer
.list_changed_counter());
499 EXPECT_EQ(0, observer
.tag_updated_counter());
502 provided_file_system_
->RemoveObserver(&observer
);
505 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_MultipleOrigins
) {
507 provided_file_system_
->AddObserver(&observer
);
510 // First watch a directory not recursively.
512 NotificationLog notification_log
;
514 provided_file_system_
->AddWatcher(
515 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
516 false /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
517 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
518 base::RunLoop().RunUntilIdle();
520 ASSERT_EQ(1u, log
.size());
521 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
522 EXPECT_EQ(1, observer
.list_changed_counter());
523 EXPECT_EQ(0, observer
.tag_updated_counter());
524 EXPECT_EQ(0u, notification_log
.size());
526 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
527 ASSERT_TRUE(watchers
);
528 ASSERT_EQ(1u, watchers
->size());
529 const auto& watcher_it
= watchers
->find(
530 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
531 false /* recursive */));
532 ASSERT_NE(watchers
->end(), watcher_it
);
534 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
535 const auto& subscriber_it
=
536 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
537 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
538 EXPECT_EQ(kOrigin
, subscriber_it
->first
.spec());
539 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
540 EXPECT_FALSE(subscriber_it
->second
.persistent
);
544 // Create another watcher, but recursive and with a different origin.
546 NotificationLog notification_log
;
548 provided_file_system_
->AddWatcher(
549 GURL(kAnotherOrigin
), base::FilePath(kDirectoryPath
),
550 true /* recursive */, false /* persistent */,
551 base::Bind(&LogStatus
, base::Unretained(&log
)),
552 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
553 base::RunLoop().RunUntilIdle();
555 ASSERT_EQ(1u, log
.size());
556 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
557 EXPECT_EQ(2, observer
.list_changed_counter());
558 EXPECT_EQ(0, observer
.tag_updated_counter());
559 EXPECT_EQ(0u, notification_log
.size());
561 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
562 ASSERT_TRUE(watchers
);
563 ASSERT_EQ(2u, watchers
->size());
564 const auto& watcher_it
= watchers
->find(
565 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
566 false /* recursive */));
567 ASSERT_NE(watchers
->end(), watcher_it
);
569 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
570 const auto& subscriber_it
=
571 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
572 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
573 EXPECT_EQ(kOrigin
, subscriber_it
->first
.spec());
574 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
575 EXPECT_FALSE(subscriber_it
->second
.persistent
);
579 // Remove the second watcher gracefully.
581 provided_file_system_
->RemoveWatcher(
582 GURL(kAnotherOrigin
), base::FilePath(kDirectoryPath
),
583 true /* recursive */, base::Bind(&LogStatus
, base::Unretained(&log
)));
584 base::RunLoop().RunUntilIdle();
586 ASSERT_EQ(1u, log
.size());
587 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
588 EXPECT_EQ(3, observer
.list_changed_counter());
589 EXPECT_EQ(0, observer
.tag_updated_counter());
591 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
592 ASSERT_TRUE(watchers
);
593 EXPECT_EQ(1u, watchers
->size());
594 const auto& watcher_it
= watchers
->find(
595 WatcherKey(base::FilePath(FILE_PATH_LITERAL(kDirectoryPath
)),
596 false /* recursive */));
597 ASSERT_NE(watchers
->end(), watcher_it
);
599 EXPECT_EQ(1u, watcher_it
->second
.subscribers
.size());
600 const auto& subscriber_it
=
601 watcher_it
->second
.subscribers
.find(GURL(kOrigin
));
602 ASSERT_NE(watcher_it
->second
.subscribers
.end(), subscriber_it
);
603 EXPECT_EQ(kOrigin
, subscriber_it
->first
.spec());
604 EXPECT_EQ(kOrigin
, subscriber_it
->second
.origin
.spec());
605 EXPECT_FALSE(subscriber_it
->second
.persistent
);
608 provided_file_system_
->RemoveObserver(&observer
);
611 TEST_F(FileSystemProviderProvidedFileSystemTest
, RemoveWatcher
) {
613 provided_file_system_
->AddObserver(&observer
);
616 // First, confirm that removing a watcher which does not exist results in an
619 provided_file_system_
->RemoveWatcher(
620 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
621 base::Bind(&LogStatus
, base::Unretained(&log
)));
622 base::RunLoop().RunUntilIdle();
624 ASSERT_EQ(1u, log
.size());
625 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, log
[0]);
626 EXPECT_EQ(0, observer
.list_changed_counter());
627 EXPECT_EQ(0, observer
.tag_updated_counter());
631 // Watch a directory not recursively.
633 NotificationLog notification_log
;
635 provided_file_system_
->AddWatcher(
636 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
637 false /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
638 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
639 base::RunLoop().RunUntilIdle();
641 ASSERT_EQ(1u, log
.size());
642 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
643 EXPECT_EQ(1, observer
.list_changed_counter());
644 EXPECT_EQ(0, observer
.tag_updated_counter());
645 EXPECT_EQ(0u, notification_log
.size());
647 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
648 EXPECT_EQ(1u, watchers
->size());
652 // Remove a watcher gracefully.
654 provided_file_system_
->RemoveWatcher(
655 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
656 base::Bind(&LogStatus
, base::Unretained(&log
)));
657 base::RunLoop().RunUntilIdle();
659 ASSERT_EQ(1u, log
.size());
660 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
661 EXPECT_EQ(2, observer
.list_changed_counter());
662 EXPECT_EQ(0, observer
.tag_updated_counter());
664 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
665 EXPECT_EQ(0u, watchers
->size());
669 // Confirm that it's possible to watch it again.
671 NotificationLog notification_log
;
673 provided_file_system_
->AddWatcher(
674 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
675 false /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
676 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
677 base::RunLoop().RunUntilIdle();
679 ASSERT_EQ(1u, log
.size());
680 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
681 EXPECT_EQ(3, observer
.list_changed_counter());
682 EXPECT_EQ(0, observer
.tag_updated_counter());
683 EXPECT_EQ(0u, notification_log
.size());
685 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
686 EXPECT_EQ(1u, watchers
->size());
690 // Finally, remove it, but with an error from extension. That should result
691 // in a removed watcher, anyway. The error code should not be passed.
692 event_router_
->set_reply_result(base::File::FILE_ERROR_FAILED
);
695 provided_file_system_
->RemoveWatcher(
696 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
697 base::Bind(&LogStatus
, base::Unretained(&log
)));
698 base::RunLoop().RunUntilIdle();
700 ASSERT_EQ(1u, log
.size());
701 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
702 EXPECT_EQ(4, observer
.list_changed_counter());
703 EXPECT_EQ(0, observer
.tag_updated_counter());
705 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
706 EXPECT_EQ(0u, watchers
->size());
709 provided_file_system_
->RemoveObserver(&observer
);
712 TEST_F(FileSystemProviderProvidedFileSystemTest
, Notify
) {
714 provided_file_system_
->AddObserver(&observer
);
715 NotificationLog notification_log
;
718 // Watch a directory.
721 provided_file_system_
->AddWatcher(
722 GURL(kOrigin
), base::FilePath(kDirectoryPath
), false /* recursive */,
723 false /* persistent */, base::Bind(&LogStatus
, base::Unretained(&log
)),
724 base::Bind(&LogNotification
, base::Unretained(¬ification_log
)));
725 base::RunLoop().RunUntilIdle();
727 ASSERT_EQ(1u, log
.size());
728 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
729 EXPECT_EQ(1, observer
.list_changed_counter());
730 EXPECT_EQ(0, observer
.tag_updated_counter());
731 EXPECT_EQ(0u, notification_log
.size());
733 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
734 EXPECT_EQ(1u, watchers
->size());
735 provided_file_system_
->GetWatchers();
736 EXPECT_EQ("", watchers
->begin()->second
.last_tag
);
740 // Notify about a change.
741 const storage::WatcherManager::ChangeType change_type
=
742 storage::WatcherManager::CHANGED
;
743 const std::string tag
= "hello-world";
746 provided_file_system_
->Notify(
747 base::FilePath(kDirectoryPath
), false /* recursive */, change_type
,
748 make_scoped_ptr(new ProvidedFileSystemObserver::Changes
), tag
,
749 base::Bind(&LogStatus
, base::Unretained(&log
)));
750 base::RunLoop().RunUntilIdle();
752 // Confirm that the notification callback was called.
753 ASSERT_EQ(1u, notification_log
.size());
754 EXPECT_EQ(change_type
, notification_log
[0]);
756 // Verify the observer event.
757 ASSERT_EQ(1u, observer
.change_events().size());
758 const Observer::ChangeEvent
* const change_event
=
759 observer
.change_events()[0];
760 EXPECT_EQ(change_type
, change_event
->change_type());
761 EXPECT_EQ(0u, change_event
->changes().size());
763 // The tag should not be updated in advance, before all observers handle
765 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
766 EXPECT_EQ(1u, watchers
->size());
767 provided_file_system_
->GetWatchers();
768 EXPECT_EQ("", watchers
->begin()->second
.last_tag
);
770 // Wait until all observers finish handling the notification.
771 observer
.CompleteOnWatcherChanged();
772 base::RunLoop().RunUntilIdle();
774 ASSERT_EQ(1u, log
.size());
775 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
777 // Confirm, that the watcher still exists, and that the tag is updated.
778 ASSERT_EQ(1u, watchers
->size());
779 EXPECT_EQ(tag
, watchers
->begin()->second
.last_tag
);
780 EXPECT_EQ(1, observer
.list_changed_counter());
781 EXPECT_EQ(1, observer
.tag_updated_counter());
785 // Notify about deleting of the watched entry.
786 const storage::WatcherManager::ChangeType change_type
=
787 storage::WatcherManager::DELETED
;
788 const ProvidedFileSystemObserver::Changes changes
;
789 const std::string tag
= "chocolate-disco";
792 provided_file_system_
->Notify(
793 base::FilePath(kDirectoryPath
), false /* recursive */, change_type
,
794 make_scoped_ptr(new ProvidedFileSystemObserver::Changes
), tag
,
795 base::Bind(&LogStatus
, base::Unretained(&log
)));
796 base::RunLoop().RunUntilIdle();
798 // Complete all change events.
799 observer
.CompleteOnWatcherChanged();
800 base::RunLoop().RunUntilIdle();
802 ASSERT_EQ(1u, log
.size());
803 EXPECT_EQ(base::File::FILE_OK
, log
[0]);
805 // Confirm that the notification callback was called.
806 ASSERT_EQ(2u, notification_log
.size());
807 EXPECT_EQ(change_type
, notification_log
[1]);
809 // Verify the observer event.
810 ASSERT_EQ(2u, observer
.change_events().size());
811 const Observer::ChangeEvent
* const change_event
=
812 observer
.change_events()[1];
813 EXPECT_EQ(change_type
, change_event
->change_type());
814 EXPECT_EQ(0u, change_event
->changes().size());
817 // Confirm, that the watcher is removed.
819 Watchers
* const watchers
= provided_file_system_
->GetWatchers();
820 EXPECT_EQ(0u, watchers
->size());
821 EXPECT_EQ(2, observer
.list_changed_counter());
822 EXPECT_EQ(2, observer
.tag_updated_counter());
825 provided_file_system_
->RemoveObserver(&observer
);
828 TEST_F(FileSystemProviderProvidedFileSystemTest
, OpenedFiles
) {
830 provided_file_system_
->AddObserver(&observer
);
833 provided_file_system_
->OpenFile(
834 base::FilePath(kFilePath
), OPEN_FILE_MODE_WRITE
,
835 base::Bind(LogOpenFile
, base::Unretained(&log
)));
836 base::RunLoop().RunUntilIdle();
838 ASSERT_EQ(1u, log
.size());
839 EXPECT_EQ(base::File::FILE_OK
, log
[0].second
);
840 const int file_handle
= log
[0].first
;
842 const OpenedFiles
& opened_files
= provided_file_system_
->GetOpenedFiles();
843 const auto opened_file_it
= opened_files
.find(file_handle
);
844 ASSERT_NE(opened_files
.end(), opened_file_it
);
845 EXPECT_EQ(kFilePath
, opened_file_it
->second
.file_path
.AsUTF8Unsafe());
846 EXPECT_EQ(OPEN_FILE_MODE_WRITE
, opened_file_it
->second
.mode
);
849 provided_file_system_
->CloseFile(
850 file_handle
, base::Bind(LogStatus
, base::Unretained(&close_log
)));
851 base::RunLoop().RunUntilIdle();
853 ASSERT_EQ(1u, close_log
.size());
854 EXPECT_EQ(base::File::FILE_OK
, close_log
[0]);
855 EXPECT_EQ(0u, opened_files
.size());
857 provided_file_system_
->RemoveObserver(&observer
);
860 TEST_F(FileSystemProviderProvidedFileSystemTest
, OpenedFiles_OpeningFailure
) {
862 provided_file_system_
->AddObserver(&observer
);
864 event_router_
->set_reply_result(base::File::FILE_ERROR_NOT_FOUND
);
867 provided_file_system_
->OpenFile(
868 base::FilePath(kFilePath
), OPEN_FILE_MODE_WRITE
,
869 base::Bind(LogOpenFile
, base::Unretained(&log
)));
870 base::RunLoop().RunUntilIdle();
872 ASSERT_EQ(1u, log
.size());
873 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, log
[0].second
);
875 const OpenedFiles
& opened_files
= provided_file_system_
->GetOpenedFiles();
876 EXPECT_EQ(0u, opened_files
.size());
878 provided_file_system_
->RemoveObserver(&observer
);
881 TEST_F(FileSystemProviderProvidedFileSystemTest
, OpenedFile_ClosingFailure
) {
883 provided_file_system_
->AddObserver(&observer
);
886 provided_file_system_
->OpenFile(
887 base::FilePath(kFilePath
), OPEN_FILE_MODE_WRITE
,
888 base::Bind(LogOpenFile
, base::Unretained(&log
)));
889 base::RunLoop().RunUntilIdle();
891 ASSERT_EQ(1u, log
.size());
892 EXPECT_EQ(base::File::FILE_OK
, log
[0].second
);
893 const int file_handle
= log
[0].first
;
895 const OpenedFiles
& opened_files
= provided_file_system_
->GetOpenedFiles();
896 const auto opened_file_it
= opened_files
.find(file_handle
);
897 ASSERT_NE(opened_files
.end(), opened_file_it
);
898 EXPECT_EQ(kFilePath
, opened_file_it
->second
.file_path
.AsUTF8Unsafe());
899 EXPECT_EQ(OPEN_FILE_MODE_WRITE
, opened_file_it
->second
.mode
);
901 // Simulate an error for closing a file. Still, the file should be closed
902 // in the C++ layer, anyway.
903 event_router_
->set_reply_result(base::File::FILE_ERROR_NOT_FOUND
);
906 provided_file_system_
->CloseFile(
907 file_handle
, base::Bind(LogStatus
, base::Unretained(&close_log
)));
908 base::RunLoop().RunUntilIdle();
910 ASSERT_EQ(1u, close_log
.size());
911 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, close_log
[0]);
912 EXPECT_EQ(0u, opened_files
.size());
914 provided_file_system_
->RemoveObserver(&observer
);
917 } // namespace file_system_provider
918 } // namespace chromeos