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/test/base/testing_browser_process.h"
22 #include "chrome/test/base/testing_pref_service_syncable.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "chrome/test/base/testing_profile_manager.h"
25 #include "components/user_prefs/user_prefs.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/manifest_constants.h"
30 #include "storage/browser/fileapi/external_mount_points.h"
31 #include "testing/gtest/include/gtest/gtest.h"
34 namespace file_system_provider
{
37 const char kExtensionId
[] = "mbflcebpggnecokmikipoihdbecnjfoj";
38 const char kDisplayName
[] = "Camera Pictures";
40 // The dot in the file system ID is there in order to check that saving to
41 // preferences works correctly. File System ID is used as a key in
42 // a base::DictionaryValue, so it has to be stored without path expansion.
43 const char kFileSystemId
[] = "camera/pictures/id .!@#$%^&*()_+";
45 // Utility observer, logging events from file_system_provider::Service.
46 class LoggingObserver
: public Observer
{
50 Event(const ProvidedFileSystemInfo
& file_system_info
,
52 base::File::Error error
)
53 : file_system_info_(file_system_info
),
58 const ProvidedFileSystemInfo
& file_system_info() const {
59 return file_system_info_
;
61 MountContext
context() const { return context_
; }
62 base::File::Error
error() const { return error_
; }
65 ProvidedFileSystemInfo file_system_info_
;
66 MountContext context_
;
67 base::File::Error error_
;
71 virtual ~LoggingObserver() {}
73 // file_system_provider::Observer overrides.
74 void OnProvidedFileSystemMount(const ProvidedFileSystemInfo
& file_system_info
,
76 base::File::Error error
) override
{
77 mounts
.push_back(Event(file_system_info
, context
, error
));
80 void OnProvidedFileSystemUnmount(
81 const ProvidedFileSystemInfo
& file_system_info
,
82 base::File::Error error
) override
{
83 // TODO(mtomasz): Split these events, as mount context doesn't make sense
85 unmounts
.push_back(Event(file_system_info
, MOUNT_CONTEXT_USER
, error
));
88 std::vector
<Event
> mounts
;
89 std::vector
<Event
> unmounts
;
91 DISALLOW_COPY_AND_ASSIGN(LoggingObserver
);
94 // Fake implementation of the registry, since it's already tested separately.
95 // For simplicity it can remember at most only one file system.
96 class FakeRegistry
: public RegistryInterface
{
99 ~FakeRegistry() override
{}
101 // RegistryInterface overrides.
102 void RememberFileSystem(const ProvidedFileSystemInfo
& file_system_info
,
103 const Watchers
& watchers
) override
{
104 file_system_info_
.reset(new ProvidedFileSystemInfo(file_system_info
));
105 watchers_
.reset(new Watchers(watchers
));
108 void ForgetFileSystem(const std::string
& extension_id
,
109 const std::string
& file_system_id
) override
{
110 if (!file_system_info_
.get() || !watchers_
.get())
112 if (file_system_info_
->extension_id() == extension_id
&&
113 file_system_info_
->file_system_id() == file_system_id
) {
114 file_system_info_
.reset();
119 scoped_ptr
<RestoredFileSystems
> RestoreFileSystems(
120 const std::string
& extension_id
) override
{
121 scoped_ptr
<RestoredFileSystems
> result(new RestoredFileSystems
);
123 if (file_system_info_
.get() && watchers_
.get()) {
124 RestoredFileSystem restored_file_system
;
125 restored_file_system
.extension_id
= file_system_info_
->extension_id();
127 MountOptions options
;
128 options
.file_system_id
= file_system_info_
->file_system_id();
129 options
.display_name
= file_system_info_
->display_name();
130 options
.writable
= file_system_info_
->writable();
131 options
.supports_notify_tag
= file_system_info_
->supports_notify_tag();
132 restored_file_system
.options
= options
;
133 restored_file_system
.watchers
= *watchers_
.get();
135 result
->push_back(restored_file_system
);
141 void UpdateWatcherTag(const ProvidedFileSystemInfo
& file_system_info
,
142 const Watcher
& watcher
) override
{
143 ASSERT_TRUE(watchers_
.get());
144 const Watchers::iterator it
=
145 watchers_
->find(WatcherKey(watcher
.entry_path
, watcher
.recursive
));
146 ASSERT_NE(watchers_
->end(), it
);
147 it
->second
.last_tag
= watcher
.last_tag
;
150 ProvidedFileSystemInfo
* const file_system_info() const {
151 return file_system_info_
.get();
153 Watchers
* const watchers() const { return watchers_
.get(); }
156 scoped_ptr
<ProvidedFileSystemInfo
> file_system_info_
;
157 scoped_ptr
<Watchers
> watchers_
;
159 DISALLOW_COPY_AND_ASSIGN(FakeRegistry
);
162 // Creates a fake extension with the specified |extension_id|.
163 scoped_refptr
<extensions::Extension
> CreateFakeExtension(
164 const std::string
& extension_id
) {
165 base::DictionaryValue manifest
;
167 manifest
.SetStringWithoutPathExpansion(extensions::manifest_keys::kVersion
,
169 manifest
.SetStringWithoutPathExpansion(extensions::manifest_keys::kName
,
171 return extensions::Extension::Create(base::FilePath(),
172 extensions::Manifest::UNPACKED
,
174 extensions::Extension::NO_FLAGS
,
181 class FileSystemProviderServiceTest
: public testing::Test
{
183 FileSystemProviderServiceTest() : profile_(NULL
) {}
185 ~FileSystemProviderServiceTest() override
{}
187 void SetUp() override
{
188 profile_manager_
.reset(
189 new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
190 ASSERT_TRUE(profile_manager_
->SetUp());
191 profile_
= profile_manager_
->CreateTestingProfile("test-user@example.com");
192 user_manager_
= new FakeChromeUserManager();
193 user_manager_
->AddUser(profile_
->GetProfileUserName());
194 user_manager_enabler_
.reset(new ScopedUserManagerEnabler(user_manager_
));
195 extension_registry_
.reset(new extensions::ExtensionRegistry(profile_
));
197 service_
.reset(new Service(profile_
, extension_registry_
.get()));
198 service_
->SetFileSystemFactoryForTesting(
199 base::Bind(&FakeProvidedFileSystem::Create
));
200 extension_
= CreateFakeExtension(kExtensionId
);
202 registry_
= new FakeRegistry
;
203 // Passes ownership to the service instance.
204 service_
->SetRegistryForTesting(make_scoped_ptr(registry_
));
206 fake_watcher_
.entry_path
= base::FilePath(FILE_PATH_LITERAL("/a/b/c"));
207 fake_watcher_
.recursive
= true;
208 fake_watcher_
.last_tag
= "hello-world";
211 content::TestBrowserThreadBundle thread_bundle_
;
212 scoped_ptr
<TestingProfileManager
> profile_manager_
;
213 TestingProfile
* profile_
;
214 FakeChromeUserManager
* user_manager_
;
215 scoped_ptr
<ScopedUserManagerEnabler
> user_manager_enabler_
;
216 scoped_ptr
<extensions::ExtensionRegistry
> extension_registry_
;
217 scoped_ptr
<Service
> service_
;
218 scoped_refptr
<extensions::Extension
> extension_
;
219 FakeRegistry
* registry_
; // Owned by Service.
220 Watcher fake_watcher_
;
223 TEST_F(FileSystemProviderServiceTest
, MountFileSystem
) {
224 LoggingObserver observer
;
225 service_
->AddObserver(&observer
);
227 EXPECT_EQ(base::File::FILE_OK
,
228 service_
->MountFileSystem(
229 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
231 ASSERT_EQ(1u, observer
.mounts
.size());
232 EXPECT_EQ(kExtensionId
, observer
.mounts
[0].file_system_info().extension_id());
233 EXPECT_EQ(kFileSystemId
,
234 observer
.mounts
[0].file_system_info().file_system_id());
235 base::FilePath expected_mount_path
=
236 util::GetMountPath(profile_
, kExtensionId
, kFileSystemId
);
237 EXPECT_EQ(expected_mount_path
.AsUTF8Unsafe(),
238 observer
.mounts
[0].file_system_info().mount_path().AsUTF8Unsafe());
239 EXPECT_EQ(kDisplayName
, observer
.mounts
[0].file_system_info().display_name());
240 EXPECT_FALSE(observer
.mounts
[0].file_system_info().writable());
241 EXPECT_FALSE(observer
.mounts
[0].file_system_info().supports_notify_tag());
242 EXPECT_EQ(base::File::FILE_OK
, observer
.mounts
[0].error());
243 EXPECT_EQ(MOUNT_CONTEXT_USER
, observer
.mounts
[0].context());
244 ASSERT_EQ(0u, observer
.unmounts
.size());
246 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
247 service_
->GetProvidedFileSystemInfoList();
248 ASSERT_EQ(1u, file_system_info_list
.size());
250 service_
->RemoveObserver(&observer
);
253 TEST_F(FileSystemProviderServiceTest
,
254 MountFileSystem_WritableAndSupportsNotifyTag
) {
255 LoggingObserver observer
;
256 service_
->AddObserver(&observer
);
258 MountOptions
options(kFileSystemId
, kDisplayName
);
259 options
.writable
= true;
260 options
.supports_notify_tag
= true;
261 EXPECT_EQ(base::File::FILE_OK
,
262 service_
->MountFileSystem(kExtensionId
, options
));
264 ASSERT_EQ(1u, observer
.mounts
.size());
265 EXPECT_TRUE(observer
.mounts
[0].file_system_info().writable());
266 EXPECT_TRUE(observer
.mounts
[0].file_system_info().supports_notify_tag());
267 ASSERT_EQ(0u, observer
.unmounts
.size());
268 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
269 service_
->GetProvidedFileSystemInfoList();
270 ASSERT_EQ(1u, file_system_info_list
.size());
272 service_
->RemoveObserver(&observer
);
275 TEST_F(FileSystemProviderServiceTest
, MountFileSystem_UniqueIds
) {
276 LoggingObserver observer
;
277 service_
->AddObserver(&observer
);
279 EXPECT_EQ(base::File::FILE_OK
,
280 service_
->MountFileSystem(
281 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
282 EXPECT_EQ(base::File::FILE_ERROR_EXISTS
,
283 service_
->MountFileSystem(
284 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
286 ASSERT_EQ(2u, observer
.mounts
.size());
287 EXPECT_EQ(base::File::FILE_OK
, observer
.mounts
[0].error());
288 EXPECT_EQ(base::File::FILE_ERROR_EXISTS
, observer
.mounts
[1].error());
290 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
291 service_
->GetProvidedFileSystemInfoList();
292 ASSERT_EQ(1u, file_system_info_list
.size());
294 service_
->RemoveObserver(&observer
);
297 TEST_F(FileSystemProviderServiceTest
, MountFileSystem_StressTest
) {
298 LoggingObserver observer
;
299 service_
->AddObserver(&observer
);
301 const size_t kMaxFileSystems
= 16;
302 for (size_t i
= 0; i
< kMaxFileSystems
; ++i
) {
303 const std::string file_system_id
=
304 std::string("test-") + base::IntToString(i
);
305 EXPECT_EQ(base::File::FILE_OK
,
306 service_
->MountFileSystem(
307 kExtensionId
, MountOptions(file_system_id
, kDisplayName
)));
309 ASSERT_EQ(kMaxFileSystems
, observer
.mounts
.size());
311 // The next file system is out of limit, and registering it should fail.
312 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED
,
313 service_
->MountFileSystem(
314 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
316 ASSERT_EQ(kMaxFileSystems
+ 1, observer
.mounts
.size());
317 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED
,
318 observer
.mounts
[kMaxFileSystems
].error());
320 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
321 service_
->GetProvidedFileSystemInfoList();
322 ASSERT_EQ(kMaxFileSystems
, file_system_info_list
.size());
324 service_
->RemoveObserver(&observer
);
327 TEST_F(FileSystemProviderServiceTest
, UnmountFileSystem
) {
328 LoggingObserver observer
;
329 service_
->AddObserver(&observer
);
331 EXPECT_EQ(base::File::FILE_OK
,
332 service_
->MountFileSystem(
333 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
334 ASSERT_EQ(1u, observer
.mounts
.size());
336 EXPECT_EQ(base::File::FILE_OK
,
337 service_
->UnmountFileSystem(kExtensionId
, kFileSystemId
,
338 Service::UNMOUNT_REASON_USER
));
339 ASSERT_EQ(1u, observer
.unmounts
.size());
340 EXPECT_EQ(base::File::FILE_OK
, observer
.unmounts
[0].error());
342 EXPECT_EQ(kExtensionId
,
343 observer
.unmounts
[0].file_system_info().extension_id());
344 EXPECT_EQ(kFileSystemId
,
345 observer
.unmounts
[0].file_system_info().file_system_id());
347 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
348 service_
->GetProvidedFileSystemInfoList();
349 ASSERT_EQ(0u, file_system_info_list
.size());
351 service_
->RemoveObserver(&observer
);
354 TEST_F(FileSystemProviderServiceTest
, UnmountFileSystem_OnExtensionUnload
) {
355 LoggingObserver observer
;
356 service_
->AddObserver(&observer
);
358 EXPECT_EQ(base::File::FILE_OK
,
359 service_
->MountFileSystem(
360 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
361 ASSERT_EQ(1u, observer
.mounts
.size());
363 // Directly call the observer's method.
364 service_
->OnExtensionUnloaded(
367 extensions::UnloadedExtensionInfo::REASON_DISABLE
);
369 ASSERT_EQ(1u, observer
.unmounts
.size());
370 EXPECT_EQ(base::File::FILE_OK
, observer
.unmounts
[0].error());
372 EXPECT_EQ(kExtensionId
,
373 observer
.unmounts
[0].file_system_info().extension_id());
374 EXPECT_EQ(kFileSystemId
,
375 observer
.unmounts
[0].file_system_info().file_system_id());
377 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
378 service_
->GetProvidedFileSystemInfoList();
379 ASSERT_EQ(0u, file_system_info_list
.size());
381 service_
->RemoveObserver(&observer
);
384 TEST_F(FileSystemProviderServiceTest
, UnmountFileSystem_WrongExtensionId
) {
385 LoggingObserver observer
;
386 service_
->AddObserver(&observer
);
388 const std::string kWrongExtensionId
= "helloworldhelloworldhelloworldhe";
390 EXPECT_EQ(base::File::FILE_OK
,
391 service_
->MountFileSystem(
392 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
393 ASSERT_EQ(1u, observer
.mounts
.size());
394 ASSERT_EQ(1u, service_
->GetProvidedFileSystemInfoList().size());
396 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
,
397 service_
->UnmountFileSystem(kWrongExtensionId
, kFileSystemId
,
398 Service::UNMOUNT_REASON_USER
));
399 ASSERT_EQ(1u, observer
.unmounts
.size());
400 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND
, observer
.unmounts
[0].error());
401 ASSERT_EQ(1u, service_
->GetProvidedFileSystemInfoList().size());
403 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
404 service_
->GetProvidedFileSystemInfoList();
405 ASSERT_EQ(1u, file_system_info_list
.size());
407 service_
->RemoveObserver(&observer
);
410 TEST_F(FileSystemProviderServiceTest
, RestoreFileSystem_OnExtensionLoad
) {
411 LoggingObserver observer
;
412 service_
->AddObserver(&observer
);
414 // Remember a fake file system first in order to be able to restore it.
415 MountOptions
options(kFileSystemId
, kDisplayName
);
416 options
.supports_notify_tag
= true;
417 ProvidedFileSystemInfo
file_system_info(
418 kExtensionId
, options
, base::FilePath(FILE_PATH_LITERAL("/a/b/c")));
419 Watchers fake_watchers
;
420 fake_watchers
[WatcherKey(fake_watcher_
.entry_path
, fake_watcher_
.recursive
)] =
422 registry_
->RememberFileSystem(file_system_info
, fake_watchers
);
424 EXPECT_EQ(0u, observer
.mounts
.size());
426 // Directly call the observer's method.
427 service_
->OnExtensionLoaded(profile_
, extension_
.get());
429 ASSERT_EQ(1u, observer
.mounts
.size());
430 EXPECT_EQ(base::File::FILE_OK
, observer
.mounts
[0].error());
431 EXPECT_EQ(MOUNT_CONTEXT_RESTORE
, observer
.mounts
[0].context());
433 EXPECT_EQ(file_system_info
.extension_id(),
434 observer
.mounts
[0].file_system_info().extension_id());
435 EXPECT_EQ(file_system_info
.file_system_id(),
436 observer
.mounts
[0].file_system_info().file_system_id());
437 EXPECT_EQ(file_system_info
.writable(),
438 observer
.mounts
[0].file_system_info().writable());
439 EXPECT_EQ(file_system_info
.supports_notify_tag(),
440 observer
.mounts
[0].file_system_info().supports_notify_tag());
442 std::vector
<ProvidedFileSystemInfo
> file_system_info_list
=
443 service_
->GetProvidedFileSystemInfoList();
444 ASSERT_EQ(1u, file_system_info_list
.size());
446 ProvidedFileSystemInterface
* const file_system
=
447 service_
->GetProvidedFileSystem(kExtensionId
, kFileSystemId
);
448 ASSERT_TRUE(file_system
);
450 const Watchers
* const watchers
= file_system
->GetWatchers();
451 ASSERT_TRUE(watchers
);
452 ASSERT_EQ(1u, watchers
->size());
454 const Watchers::const_iterator restored_watcher_it
= watchers
->find(
455 WatcherKey(fake_watcher_
.entry_path
, fake_watcher_
.recursive
));
456 ASSERT_NE(watchers
->end(), restored_watcher_it
);
458 EXPECT_EQ(fake_watcher_
.entry_path
, restored_watcher_it
->second
.entry_path
);
459 EXPECT_EQ(fake_watcher_
.recursive
, restored_watcher_it
->second
.recursive
);
460 EXPECT_EQ(fake_watcher_
.last_tag
, restored_watcher_it
->second
.last_tag
);
462 service_
->RemoveObserver(&observer
);
465 TEST_F(FileSystemProviderServiceTest
, RememberFileSystem_OnMount
) {
466 LoggingObserver observer
;
467 service_
->AddObserver(&observer
);
469 EXPECT_FALSE(registry_
->file_system_info());
470 EXPECT_FALSE(registry_
->watchers());
472 EXPECT_EQ(base::File::FILE_OK
,
473 service_
->MountFileSystem(
474 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
475 ASSERT_EQ(1u, observer
.mounts
.size());
477 ASSERT_TRUE(registry_
->file_system_info());
478 EXPECT_EQ(kExtensionId
, registry_
->file_system_info()->extension_id());
479 EXPECT_EQ(kFileSystemId
, registry_
->file_system_info()->file_system_id());
480 EXPECT_EQ(kDisplayName
, registry_
->file_system_info()->display_name());
481 EXPECT_FALSE(registry_
->file_system_info()->writable());
482 EXPECT_FALSE(registry_
->file_system_info()->supports_notify_tag());
483 ASSERT_TRUE(registry_
->watchers());
485 service_
->RemoveObserver(&observer
);
488 TEST_F(FileSystemProviderServiceTest
, RememberFileSystem_OnUnmountOnShutdown
) {
489 LoggingObserver observer
;
490 service_
->AddObserver(&observer
);
493 EXPECT_FALSE(registry_
->file_system_info());
494 EXPECT_FALSE(registry_
->watchers());
495 EXPECT_EQ(base::File::FILE_OK
,
496 service_
->MountFileSystem(
497 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
499 EXPECT_EQ(1u, observer
.mounts
.size());
500 EXPECT_TRUE(registry_
->file_system_info());
501 EXPECT_TRUE(registry_
->watchers());
505 EXPECT_EQ(base::File::FILE_OK
,
506 service_
->UnmountFileSystem(kExtensionId
, kFileSystemId
,
507 Service::UNMOUNT_REASON_SHUTDOWN
));
509 EXPECT_EQ(1u, observer
.unmounts
.size());
510 EXPECT_TRUE(registry_
->file_system_info());
511 EXPECT_TRUE(registry_
->watchers());
514 service_
->RemoveObserver(&observer
);
517 TEST_F(FileSystemProviderServiceTest
, RememberFileSystem_OnUnmountByUser
) {
518 LoggingObserver observer
;
519 service_
->AddObserver(&observer
);
522 EXPECT_FALSE(registry_
->file_system_info());
523 EXPECT_FALSE(registry_
->watchers());
524 EXPECT_EQ(base::File::FILE_OK
,
525 service_
->MountFileSystem(
526 kExtensionId
, MountOptions(kFileSystemId
, kDisplayName
)));
528 EXPECT_EQ(1u, observer
.mounts
.size());
529 EXPECT_TRUE(registry_
->file_system_info());
530 EXPECT_TRUE(registry_
->watchers());
534 EXPECT_EQ(base::File::FILE_OK
,
535 service_
->UnmountFileSystem(kExtensionId
, kFileSystemId
,
536 Service::UNMOUNT_REASON_USER
));
538 EXPECT_EQ(1u, observer
.unmounts
.size());
539 EXPECT_FALSE(registry_
->file_system_info());
540 EXPECT_FALSE(registry_
->watchers());
543 service_
->RemoveObserver(&observer
);
546 } // namespace file_system_provider
547 } // namespace chromeos