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/service.h"
10 #include "base/files/file.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h"
15 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h"
16 #include "chrome/browser/chromeos/file_system_provider/observer.h"
17 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
18 #include "chrome/browser/chromeos/file_system_provider/registry_interface.h"
19 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
20 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
21 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
22 #include "chrome/test/base/testing_browser_process.h"
23 #include "chrome/test/base/testing_pref_service_syncable.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "chrome/test/base/testing_profile_manager.h"
26 #include "components/user_prefs/user_prefs.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/manifest_constants.h"
31 #include "storage/browser/fileapi/external_mount_points.h"
32 #include "testing/gtest/include/gtest/gtest.h"
35 namespace file_system_provider
{
38 const char kExtensionId
[] = "mbflcebpggnecokmikipoihdbecnjfoj";
39 const char kDisplayName
[] = "Camera Pictures";
41 // The dot in the file system ID is there in order to check that saving to
42 // preferences works correctly. File System ID is used as a key in
43 // a base::DictionaryValue, so it has to be stored without path expansion.
44 const char kFileSystemId
[] = "camera/pictures/id .!@#$%^&*()_+";
46 // Utility observer, logging events from file_system_provider::Service.
47 class LoggingObserver
: public Observer
{
51 Event(const ProvidedFileSystemInfo
& file_system_info
,
53 base::File::Error error
)
54 : file_system_info_(file_system_info
),
59 const ProvidedFileSystemInfo
& file_system_info() const {
60 return file_system_info_
;
62 MountContext
context() const { return context_
; }
63 base::File::Error
error() const { return error_
; }
66 ProvidedFileSystemInfo file_system_info_
;
67 MountContext context_
;
68 base::File::Error error_
;
72 ~LoggingObserver() override
{}
74 // file_system_provider::Observer overrides.
75 void OnProvidedFileSystemMount(const ProvidedFileSystemInfo
& file_system_info
,
77 base::File::Error error
) override
{
78 mounts
.push_back(Event(file_system_info
, context
, error
));
81 void OnProvidedFileSystemUnmount(
82 const ProvidedFileSystemInfo
& file_system_info
,
83 base::File::Error error
) override
{
84 // TODO(mtomasz): Split these events, as mount context doesn't make sense
86 unmounts
.push_back(Event(file_system_info
, MOUNT_CONTEXT_USER
, error
));
89 std::vector
<Event
> mounts
;
90 std::vector
<Event
> unmounts
;
92 DISALLOW_COPY_AND_ASSIGN(LoggingObserver
);
95 // Fake implementation of the registry, since it's already tested separately.
96 // For simplicity it can remember at most only one file system.
97 class FakeRegistry
: public RegistryInterface
{
100 ~FakeRegistry() override
{}
102 // RegistryInterface overrides.
103 void RememberFileSystem(const ProvidedFileSystemInfo
& file_system_info
,
104 const Watchers
& watchers
) override
{
105 file_system_info_
.reset(new ProvidedFileSystemInfo(file_system_info
));
106 watchers_
.reset(new Watchers(watchers
));
109 void ForgetFileSystem(const std::string
& extension_id
,
110 const std::string
& file_system_id
) override
{
111 if (!file_system_info_
.get() || !watchers_
.get())
113 if (file_system_info_
->extension_id() == extension_id
&&
114 file_system_info_
->file_system_id() == file_system_id
) {
115 file_system_info_
.reset();
120 scoped_ptr
<RestoredFileSystems
> RestoreFileSystems(
121 const std::string
& extension_id
) override
{
122 scoped_ptr
<RestoredFileSystems
> result(new RestoredFileSystems
);
124 if (file_system_info_
.get() && watchers_
.get()) {
125 RestoredFileSystem restored_file_system
;
126 restored_file_system
.extension_id
= file_system_info_
->extension_id();
128 MountOptions options
;
129 options
.file_system_id
= file_system_info_
->file_system_id();
130 options
.display_name
= file_system_info_
->display_name();
131 options
.writable
= file_system_info_
->writable();
132 options
.supports_notify_tag
= file_system_info_
->supports_notify_tag();
133 restored_file_system
.options
= options
;
134 restored_file_system
.watchers
= *watchers_
.get();
136 result
->push_back(restored_file_system
);
142 void UpdateWatcherTag(const ProvidedFileSystemInfo
& file_system_info
,
143 const Watcher
& watcher
) override
{
144 ASSERT_TRUE(watchers_
.get());
145 const Watchers::iterator it
=
146 watchers_
->find(WatcherKey(watcher
.entry_path
, watcher
.recursive
));
147 ASSERT_NE(watchers_
->end(), it
);
148 it
->second
.last_tag
= watcher
.last_tag
;
151 ProvidedFileSystemInfo
* const file_system_info() const {
152 return file_system_info_
.get();
154 Watchers
* const watchers() const { return watchers_
.get(); }
157 scoped_ptr
<ProvidedFileSystemInfo
> file_system_info_
;
158 scoped_ptr
<Watchers
> watchers_
;
160 DISALLOW_COPY_AND_ASSIGN(FakeRegistry
);
163 // Creates a fake extension with the specified |extension_id|.
164 scoped_refptr
<extensions::Extension
> CreateFakeExtension(
165 const std::string
& extension_id
) {
166 base::DictionaryValue manifest
;
168 manifest
.SetStringWithoutPathExpansion(extensions::manifest_keys::kVersion
,
170 manifest
.SetStringWithoutPathExpansion(extensions::manifest_keys::kName
,
172 return extensions::Extension::Create(base::FilePath(),
173 extensions::Manifest::UNPACKED
,
175 extensions::Extension::NO_FLAGS
,
182 class FileSystemProviderServiceTest
: public testing::Test
{
184 FileSystemProviderServiceTest() : profile_(NULL
) {}
186 ~FileSystemProviderServiceTest() override
{}
188 void SetUp() override
{
189 profile_manager_
.reset(
190 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
191 ASSERT_TRUE(profile_manager_
->SetUp());
192 profile_
= profile_manager_
->CreateTestingProfile("test-user@example.com");
193 user_manager_
= new FakeChromeUserManager();
194 user_manager_
->AddUser(profile_
->GetProfileUserName());
195 user_manager_enabler_
.reset(new ScopedUserManagerEnabler(user_manager_
));
196 extension_registry_
.reset(new extensions::ExtensionRegistry(profile_
));
198 service_
.reset(new Service(profile_
, extension_registry_
.get()));
199 service_
->SetFileSystemFactoryForTesting(
200 base::Bind(&FakeProvidedFileSystem::Create
));
201 extension_
= CreateFakeExtension(kExtensionId
);
203 registry_
= new FakeRegistry
;
204 // Passes ownership to the service instance.
205 service_
->SetRegistryForTesting(make_scoped_ptr(registry_
));
207 fake_watcher_
.entry_path
= base::FilePath(FILE_PATH_LITERAL("/a/b/c"));
208 fake_watcher_
.recursive
= true;
209 fake_watcher_
.last_tag
= "hello-world";
212 content::TestBrowserThreadBundle thread_bundle_
;
213 scoped_ptr
<TestingProfileManager
> profile_manager_
;
214 TestingProfile
* profile_
;
215 FakeChromeUserManager
* user_manager_
;
216 scoped_ptr
<ScopedUserManagerEnabler
> user_manager_enabler_
;
217 scoped_ptr
<extensions::ExtensionRegistry
> extension_registry_
;
218 scoped_ptr
<Service
> service_
;
219 scoped_refptr
<extensions::Extension
> extension_
;
220 FakeRegistry
* registry_
; // Owned by Service.
221 Watcher fake_watcher_
;
224 TEST_F(FileSystemProviderServiceTest
, MountFileSystem
) {
225 LoggingObserver observer
;
226 service_
->AddObserver(&observer
);
228 EXPECT_EQ(base::File::FILE_OK
,
229 service_
->MountFileSystem(
230 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
232 ASSERT_EQ(1u, observer
.mounts
.size());
233 EXPECT_EQ(kExtensionId
, observer
.mounts
[0].file_system_info().extension_id());
234 EXPECT_EQ(kFileSystemId
,
235 observer
.mounts
[0].file_system_info().file_system_id());
236 base::FilePath expected_mount_path
=
237 util::GetMountPath(profile_
, kExtensionId
, kFileSystemId
);
238 EXPECT_EQ(expected_mount_path
.AsUTF8Unsafe(),
239 observer
.mounts
[0].file_system_info().mount_path().AsUTF8Unsafe());
240 EXPECT_EQ(kDisplayName
, observer
.mounts
[0].file_system_info().display_name());
241 EXPECT_FALSE(observer
.mounts
[0].file_system_info().writable());
242 EXPECT_FALSE(observer
.mounts
[0].file_system_info().supports_notify_tag());
243 EXPECT_EQ(base::File::FILE_OK
, observer
.mounts
[0].error());
244 EXPECT_EQ(MOUNT_CONTEXT_USER
, observer
.mounts
[0].context());
245 ASSERT_EQ(0u, observer
.unmounts
.size());
247 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
248 service_
->GetProvidedFileSystemInfoList();
249 ASSERT_EQ(1u, file_system_info_list
.size());
251 service_
->RemoveObserver(&observer
);
254 TEST_F(FileSystemProviderServiceTest
,
255 MountFileSystem_WritableAndSupportsNotifyTag
) {
256 LoggingObserver observer
;
257 service_
->AddObserver(&observer
);
259 MountOptions
options(kFileSystemId
, kDisplayName
);
260 options
.writable
= true;
261 options
.supports_notify_tag
= true;
262 EXPECT_EQ(base::File::FILE_OK
,
263 service_
->MountFileSystem(kExtensionId
, options
));
265 ASSERT_EQ(1u, observer
.mounts
.size());
266 EXPECT_TRUE(observer
.mounts
[0].file_system_info().writable());
267 EXPECT_TRUE(observer
.mounts
[0].file_system_info().supports_notify_tag());
268 ASSERT_EQ(0u, observer
.unmounts
.size());
269 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
270 service_
->GetProvidedFileSystemInfoList();
271 ASSERT_EQ(1u, file_system_info_list
.size());
273 service_
->RemoveObserver(&observer
);
276 TEST_F(FileSystemProviderServiceTest
, MountFileSystem_UniqueIds
) {
277 LoggingObserver observer
;
278 service_
->AddObserver(&observer
);
280 EXPECT_EQ(base::File::FILE_OK
,
281 service_
->MountFileSystem(
282 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
283 EXPECT_EQ(base::File::FILE_ERROR_EXISTS
,
284 service_
->MountFileSystem(
285 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
287 ASSERT_EQ(2u, observer
.mounts
.size());
288 EXPECT_EQ(base::File::FILE_OK
, observer
.mounts
[0].error());
289 EXPECT_EQ(base::File::FILE_ERROR_EXISTS
, observer
.mounts
[1].error());
291 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
292 service_
->GetProvidedFileSystemInfoList();
293 ASSERT_EQ(1u, file_system_info_list
.size());
295 service_
->RemoveObserver(&observer
);
298 TEST_F(FileSystemProviderServiceTest
, MountFileSystem_StressTest
) {
299 LoggingObserver observer
;
300 service_
->AddObserver(&observer
);
302 const size_t kMaxFileSystems
= 16;
303 for (size_t i
= 0; i
< kMaxFileSystems
; ++i
) {
304 const std::string file_system_id
=
305 std::string("test-") + base::IntToString(i
);
306 EXPECT_EQ(base::File::FILE_OK
,
307 service_
->MountFileSystem(
308 kExtensionId
, MountOptions(file_system_id
, kDisplayName
)));
310 ASSERT_EQ(kMaxFileSystems
, observer
.mounts
.size());
312 // The next file system is out of limit, and registering it should fail.
313 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED
,
314 service_
->MountFileSystem(
315 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
317 ASSERT_EQ(kMaxFileSystems
+ 1, observer
.mounts
.size());
318 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED
,
319 observer
.mounts
[kMaxFileSystems
].error());
321 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
322 service_
->GetProvidedFileSystemInfoList();
323 ASSERT_EQ(kMaxFileSystems
, file_system_info_list
.size());
325 service_
->RemoveObserver(&observer
);
328 TEST_F(FileSystemProviderServiceTest
, UnmountFileSystem
) {
329 LoggingObserver observer
;
330 service_
->AddObserver(&observer
);
332 EXPECT_EQ(base::File::FILE_OK
,
333 service_
->MountFileSystem(
334 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
335 ASSERT_EQ(1u, observer
.mounts
.size());
337 EXPECT_EQ(base::File::FILE_OK
,
338 service_
->UnmountFileSystem(kExtensionId
, kFileSystemId
,
339 Service::UNMOUNT_REASON_USER
));
340 ASSERT_EQ(1u, observer
.unmounts
.size());
341 EXPECT_EQ(base::File::FILE_OK
, observer
.unmounts
[0].error());
343 EXPECT_EQ(kExtensionId
,
344 observer
.unmounts
[0].file_system_info().extension_id());
345 EXPECT_EQ(kFileSystemId
,
346 observer
.unmounts
[0].file_system_info().file_system_id());
348 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
349 service_
->GetProvidedFileSystemInfoList();
350 ASSERT_EQ(0u, file_system_info_list
.size());
352 service_
->RemoveObserver(&observer
);
355 TEST_F(FileSystemProviderServiceTest
, UnmountFileSystem_OnExtensionUnload
) {
356 LoggingObserver observer
;
357 service_
->AddObserver(&observer
);
359 EXPECT_EQ(base::File::FILE_OK
,
360 service_
->MountFileSystem(
361 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
362 ASSERT_EQ(1u, observer
.mounts
.size());
364 // Directly call the observer's method.
365 service_
->OnExtensionUnloaded(
368 extensions::UnloadedExtensionInfo::REASON_DISABLE
);
370 ASSERT_EQ(1u, observer
.unmounts
.size());
371 EXPECT_EQ(base::File::FILE_OK
, observer
.unmounts
[0].error());
373 EXPECT_EQ(kExtensionId
,
374 observer
.unmounts
[0].file_system_info().extension_id());
375 EXPECT_EQ(kFileSystemId
,
376 observer
.unmounts
[0].file_system_info().file_system_id());
378 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
379 service_
->GetProvidedFileSystemInfoList();
380 ASSERT_EQ(0u, file_system_info_list
.size());
382 service_
->RemoveObserver(&observer
);
385 TEST_F(FileSystemProviderServiceTest
, UnmountFileSystem_WrongExtensionId
) {
386 LoggingObserver observer
;
387 service_
->AddObserver(&observer
);
389 const std::string kWrongExtensionId
= "helloworldhelloworldhelloworldhe";
391 EXPECT_EQ(base::File::FILE_OK
,
392 service_
->MountFileSystem(
393 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
394 ASSERT_EQ(1u, observer
.mounts
.size());
395 ASSERT_EQ(1u, service_
->GetProvidedFileSystemInfoList().size());
397 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
398 service_
->UnmountFileSystem(kWrongExtensionId
, kFileSystemId
,
399 Service::UNMOUNT_REASON_USER
));
400 ASSERT_EQ(1u, observer
.unmounts
.size());
401 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, observer
.unmounts
[0].error());
402 ASSERT_EQ(1u, service_
->GetProvidedFileSystemInfoList().size());
404 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
405 service_
->GetProvidedFileSystemInfoList();
406 ASSERT_EQ(1u, file_system_info_list
.size());
408 service_
->RemoveObserver(&observer
);
411 TEST_F(FileSystemProviderServiceTest
, RestoreFileSystem_OnExtensionLoad
) {
412 LoggingObserver observer
;
413 service_
->AddObserver(&observer
);
415 // Remember a fake file system first in order to be able to restore it.
416 MountOptions
options(kFileSystemId
, kDisplayName
);
417 options
.supports_notify_tag
= true;
418 ProvidedFileSystemInfo
file_system_info(
419 kExtensionId
, options
, base::FilePath(FILE_PATH_LITERAL("/a/b/c")),
420 false /* configurable */, false /* watchable */, extensions::SOURCE_FILE
);
421 Watchers fake_watchers
;
422 fake_watchers
[WatcherKey(fake_watcher_
.entry_path
, fake_watcher_
.recursive
)] =
424 registry_
->RememberFileSystem(file_system_info
, fake_watchers
);
426 EXPECT_EQ(0u, observer
.mounts
.size());
428 // Directly call the observer's method.
429 service_
->OnExtensionLoaded(profile_
, extension_
.get());
431 ASSERT_EQ(1u, observer
.mounts
.size());
432 EXPECT_EQ(base::File::FILE_OK
, observer
.mounts
[0].error());
433 EXPECT_EQ(MOUNT_CONTEXT_RESTORE
, observer
.mounts
[0].context());
435 EXPECT_EQ(file_system_info
.extension_id(),
436 observer
.mounts
[0].file_system_info().extension_id());
437 EXPECT_EQ(file_system_info
.file_system_id(),
438 observer
.mounts
[0].file_system_info().file_system_id());
439 EXPECT_EQ(file_system_info
.writable(),
440 observer
.mounts
[0].file_system_info().watchable());
441 EXPECT_EQ(file_system_info
.supports_notify_tag(),
442 observer
.mounts
[0].file_system_info().supports_notify_tag());
444 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
445 service_
->GetProvidedFileSystemInfoList();
446 ASSERT_EQ(1u, file_system_info_list
.size());
448 ProvidedFileSystemInterface
* const file_system
=
449 service_
->GetProvidedFileSystem(kExtensionId
, kFileSystemId
);
450 ASSERT_TRUE(file_system
);
452 const Watchers
* const watchers
= file_system
->GetWatchers();
453 ASSERT_TRUE(watchers
);
454 ASSERT_EQ(1u, watchers
->size());
456 const Watchers::const_iterator restored_watcher_it
= watchers
->find(
457 WatcherKey(fake_watcher_
.entry_path
, fake_watcher_
.recursive
));
458 ASSERT_NE(watchers
->end(), restored_watcher_it
);
460 EXPECT_EQ(fake_watcher_
.entry_path
, restored_watcher_it
->second
.entry_path
);
461 EXPECT_EQ(fake_watcher_
.recursive
, restored_watcher_it
->second
.recursive
);
462 EXPECT_EQ(fake_watcher_
.last_tag
, restored_watcher_it
->second
.last_tag
);
464 service_
->RemoveObserver(&observer
);
467 TEST_F(FileSystemProviderServiceTest
, RememberFileSystem_OnMount
) {
468 LoggingObserver observer
;
469 service_
->AddObserver(&observer
);
471 EXPECT_FALSE(registry_
->file_system_info());
472 EXPECT_FALSE(registry_
->watchers());
474 EXPECT_EQ(base::File::FILE_OK
,
475 service_
->MountFileSystem(
476 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
477 ASSERT_EQ(1u, observer
.mounts
.size());
479 ASSERT_TRUE(registry_
->file_system_info());
480 EXPECT_EQ(kExtensionId
, registry_
->file_system_info()->extension_id());
481 EXPECT_EQ(kFileSystemId
, registry_
->file_system_info()->file_system_id());
482 EXPECT_EQ(kDisplayName
, registry_
->file_system_info()->display_name());
483 EXPECT_FALSE(registry_
->file_system_info()->writable());
484 EXPECT_FALSE(registry_
->file_system_info()->configurable());
485 EXPECT_FALSE(registry_
->file_system_info()->watchable());
486 EXPECT_FALSE(registry_
->file_system_info()->supports_notify_tag());
487 ASSERT_TRUE(registry_
->watchers());
489 service_
->RemoveObserver(&observer
);
492 TEST_F(FileSystemProviderServiceTest
, RememberFileSystem_OnUnmountOnShutdown
) {
493 LoggingObserver observer
;
494 service_
->AddObserver(&observer
);
497 EXPECT_FALSE(registry_
->file_system_info());
498 EXPECT_FALSE(registry_
->watchers());
499 EXPECT_EQ(base::File::FILE_OK
,
500 service_
->MountFileSystem(
501 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
503 EXPECT_EQ(1u, observer
.mounts
.size());
504 EXPECT_TRUE(registry_
->file_system_info());
505 EXPECT_TRUE(registry_
->watchers());
509 EXPECT_EQ(base::File::FILE_OK
,
510 service_
->UnmountFileSystem(kExtensionId
, kFileSystemId
,
511 Service::UNMOUNT_REASON_SHUTDOWN
));
513 EXPECT_EQ(1u, observer
.unmounts
.size());
514 EXPECT_TRUE(registry_
->file_system_info());
515 EXPECT_TRUE(registry_
->watchers());
518 service_
->RemoveObserver(&observer
);
521 TEST_F(FileSystemProviderServiceTest
, RememberFileSystem_OnUnmountByUser
) {
522 LoggingObserver observer
;
523 service_
->AddObserver(&observer
);
526 EXPECT_FALSE(registry_
->file_system_info());
527 EXPECT_FALSE(registry_
->watchers());
528 EXPECT_EQ(base::File::FILE_OK
,
529 service_
->MountFileSystem(
530 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
532 EXPECT_EQ(1u, observer
.mounts
.size());
533 EXPECT_TRUE(registry_
->file_system_info());
534 EXPECT_TRUE(registry_
->watchers());
538 EXPECT_EQ(base::File::FILE_OK
,
539 service_
->UnmountFileSystem(kExtensionId
, kFileSystemId
,
540 Service::UNMOUNT_REASON_USER
));
542 EXPECT_EQ(1u, observer
.unmounts
.size());
543 EXPECT_FALSE(registry_
->file_system_info());
544 EXPECT_FALSE(registry_
->watchers());
547 service_
->RemoveObserver(&observer
);
550 } // namespace file_system_provider
551 } // namespace chromeos