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_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"
34 namespace file_system_provider
{
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
{
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
);
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(
93 RequestValue::CreateForOperationSuccess(params
.Pass()),
94 false /* has_more */);
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
; }
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
{
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 {
122 const ProvidedFileSystemObserver::Changes
& changes() const {
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
,
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_
; }
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
{
184 StubNotificationManager() {}
185 ~StubNotificationManager() override
{}
187 // NotificationManagerInterface overrides.
188 void ShowUnresponsiveNotification(
190 const NotificationCallback
& callback
) override
{}
191 void HideUnresponsiveNotification(int id
) override
{}
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
,
215 base::File::Error result
) {
216 open_file_log
->push_back(std::make_pair(file_handle
, result
));
221 class FileSystemProviderProvidedFileSystemTest
: public testing::Test
{
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()));
241 new FakeEventRouter(profile_
.get(), provided_file_system_
.get()));
242 event_router_
->AddEventListener(extensions::api::file_system_provider::
243 OnAddWatcherRequested::kEventName
,
246 event_router_
->AddEventListener(extensions::api::file_system_provider::
247 OnRemoveWatcherRequested::kEventName
,
250 event_router_
->AddEventListener(
251 extensions::api::file_system_provider::OnOpenFileRequested::kEventName
,
253 event_router_
->AddEventListener(
254 extensions::api::file_system_provider::OnCloseFileRequested::kEventName
,
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
) {
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
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
) {
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
) {
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
314 base::RunLoop().RunUntilIdle();
315 EXPECT_EQ(0u, log
.size());
318 TEST_F(FileSystemProviderProvidedFileSystemTest
, AddWatcher_NotFound
) {
320 NotificationLog notification_log
;
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(¬ification_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
) {
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.
381 NotificationLog notification_log
;
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(¬ification_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.
407 // Create a provided file system interface, which does not support a notify
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(),
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
) {
443 provided_file_system_
->AddObserver(&observer
);
446 // First watch a directory not recursively.
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.
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.
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
) {
510 provided_file_system_
->AddObserver(&observer
);
513 // First watch a directory not recursively.
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(¬ification_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.
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(¬ification_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.
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
) {
616 provided_file_system_
->AddObserver(&observer
);
619 // First, confirm that removing a watcher which does not exist results in an
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.
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(¬ification_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.
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.
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(¬ification_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
);
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
) {
717 provided_file_system_
->AddObserver(&observer
);
718 NotificationLog notification_log
;
721 // Watch a directory.
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(¬ification_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";
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
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";
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
) {
833 provided_file_system_
->AddObserver(&observer
);
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
);
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
) {
865 provided_file_system_
->AddObserver(&observer
);
867 event_router_
->set_reply_result(base::File::FILE_ERROR_NOT_FOUND
);
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
) {
886 provided_file_system_
->AddObserver(&observer
);
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
);
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