1 // Copyright (c) 2012 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 "base/run_loop.h"
6 #include "base/stl_util.h"
7 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
8 #include "chrome/browser/chromeos/file_manager/file_watcher.h"
9 #include "chrome/browser/chromeos/file_manager/mount_test_util.h"
10 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
11 #include "chrome/browser/extensions/extension_apitest.h"
12 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
13 #include "chrome/test/base/testing_profile.h"
14 #include "chrome/test/base/ui_test_utils.h"
15 #include "chromeos/dbus/cros_disks_client.h"
16 #include "chromeos/disks/mock_disk_mount_manager.h"
17 #include "components/drive/file_change.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/common/install_warning.h"
20 #include "google_apis/drive/test_util.h"
21 #include "storage/browser/fileapi/external_mount_points.h"
24 using ::testing::ReturnRef
;
26 using chromeos::disks::DiskMountManager
;
31 const char* system_path
;
32 const char* file_path
;
33 const char* device_label
;
34 const char* drive_label
;
35 const char* vendor_id
;
36 const char* vendor_name
;
37 const char* product_id
;
38 const char* product_name
;
40 const char* system_path_prefix
;
41 chromeos::DeviceType device_type
;
47 bool on_removable_device
;
51 struct TestMountPoint
{
52 std::string source_path
;
53 std::string mount_path
;
54 chromeos::MountType mount_type
;
55 chromeos::disks::MountCondition mount_condition
;
57 // -1 if there is no disk info.
61 TestDiskInfo kTestDisks
[] = {
72 "system_path_prefix1",
73 chromeos::DEVICE_TYPE_USB
,
92 "system_path_prefix2",
93 chromeos::DEVICE_TYPE_MOBILE
,
112 "system_path_prefix3",
113 chromeos::DEVICE_TYPE_OPTICAL_DISC
,
124 void DispatchDirectoryChangeEventImpl(
126 const base::FilePath
& virtual_path
,
127 const drive::FileChange
* list
,
129 const std::vector
<std::string
>& extension_ids
) {
133 void AddFileWatchCallback(bool success
) {}
135 bool InitializeLocalFileSystem(std::string mount_point_name
,
136 base::ScopedTempDir
* temp_dir
,
137 base::FilePath
* mount_point_dir
) {
138 const char kTestFileContent
[] = "The five boxing wizards jumped quickly";
139 if (!temp_dir
->CreateUniqueTempDir())
142 *mount_point_dir
= temp_dir
->path().AppendASCII(mount_point_name
);
143 // Create the mount point.
144 if (!base::CreateDirectory(*mount_point_dir
))
147 const base::FilePath test_dir
= mount_point_dir
->AppendASCII("test_dir");
148 if (!base::CreateDirectory(test_dir
))
151 const base::FilePath test_file
= test_dir
.AppendASCII("test_file.txt");
152 if (!google_apis::test_util::WriteStringToFile(test_file
, kTestFileContent
))
160 class FileManagerPrivateApiTest
: public ExtensionApiTest
{
162 FileManagerPrivateApiTest()
163 : disk_mount_manager_mock_(NULL
) {
167 ~FileManagerPrivateApiTest() override
{
168 DCHECK(!disk_mount_manager_mock_
);
169 DCHECK(!testing_profile_
);
170 DCHECK(!event_router_
);
171 STLDeleteValues(&volumes_
);
174 void SetUpOnMainThread() override
{
175 ExtensionApiTest::SetUpOnMainThread();
177 testing_profile_
.reset(new TestingProfile());
178 event_router_
.reset(new file_manager::EventRouter(testing_profile_
.get()));
181 void TearDownOnMainThread() override
{
182 event_router_
->Shutdown();
184 event_router_
.reset();
185 testing_profile_
.reset();
187 ExtensionApiTest::TearDownOnMainThread();
190 // ExtensionApiTest override
191 void SetUpInProcessBrowserTestFixture() override
{
192 ExtensionApiTest::SetUpInProcessBrowserTestFixture();
193 disk_mount_manager_mock_
= new chromeos::disks::MockDiskMountManager
;
194 chromeos::disks::DiskMountManager::InitializeForTesting(
195 disk_mount_manager_mock_
);
196 disk_mount_manager_mock_
->SetupDefaultReplies();
198 // override mock functions.
199 ON_CALL(*disk_mount_manager_mock_
, FindDiskBySourcePath(_
)).WillByDefault(
200 Invoke(this, &FileManagerPrivateApiTest::FindVolumeBySourcePath
));
201 EXPECT_CALL(*disk_mount_manager_mock_
, disks())
202 .WillRepeatedly(ReturnRef(volumes_
));
203 EXPECT_CALL(*disk_mount_manager_mock_
, mount_points())
204 .WillRepeatedly(ReturnRef(mount_points_
));
207 // ExtensionApiTest override
208 void TearDownInProcessBrowserTestFixture() override
{
209 chromeos::disks::DiskMountManager::Shutdown();
210 disk_mount_manager_mock_
= NULL
;
212 ExtensionApiTest::TearDownInProcessBrowserTestFixture();
216 void InitMountPoints() {
217 const TestMountPoint kTestMountPoints
[] = {
220 chromeos::CrosDisksClient::GetRemovableDiskMountPoint().AppendASCII(
221 "mount_path1").AsUTF8Unsafe(),
222 chromeos::MOUNT_TYPE_DEVICE
,
223 chromeos::disks::MOUNT_CONDITION_NONE
,
228 chromeos::CrosDisksClient::GetRemovableDiskMountPoint().AppendASCII(
229 "mount_path2").AsUTF8Unsafe(),
230 chromeos::MOUNT_TYPE_DEVICE
,
231 chromeos::disks::MOUNT_CONDITION_NONE
,
236 chromeos::CrosDisksClient::GetRemovableDiskMountPoint().AppendASCII(
237 "mount_path3").AsUTF8Unsafe(),
238 chromeos::MOUNT_TYPE_DEVICE
,
239 chromeos::disks::MOUNT_CONDITION_NONE
,
243 // Set source path inside another mounted volume.
244 chromeos::CrosDisksClient::GetRemovableDiskMountPoint().AppendASCII(
245 "mount_path3/archive.zip").AsUTF8Unsafe(),
246 chromeos::CrosDisksClient::GetArchiveMountPoint().AppendASCII(
247 "archive_mount_path").AsUTF8Unsafe(),
248 chromeos::MOUNT_TYPE_ARCHIVE
,
249 chromeos::disks::MOUNT_CONDITION_NONE
,
254 for (size_t i
= 0; i
< arraysize(kTestMountPoints
); i
++) {
255 mount_points_
.insert(DiskMountManager::MountPointMap::value_type(
256 kTestMountPoints
[i
].mount_path
,
257 DiskMountManager::MountPointInfo(kTestMountPoints
[i
].source_path
,
258 kTestMountPoints
[i
].mount_path
,
259 kTestMountPoints
[i
].mount_type
,
260 kTestMountPoints
[i
].mount_condition
)
262 int disk_info_index
= kTestMountPoints
[i
].disk_info_index
;
263 if (kTestMountPoints
[i
].disk_info_index
>= 0) {
264 EXPECT_GT(arraysize(kTestDisks
), static_cast<size_t>(disk_info_index
));
265 if (static_cast<size_t>(disk_info_index
) >= arraysize(kTestDisks
))
268 volumes_
.insert(DiskMountManager::DiskMap::value_type(
269 kTestMountPoints
[i
].source_path
,
270 new DiskMountManager::Disk(
271 kTestMountPoints
[i
].source_path
,
272 kTestMountPoints
[i
].mount_path
,
273 kTestDisks
[disk_info_index
].system_path
,
274 kTestDisks
[disk_info_index
].file_path
,
275 kTestDisks
[disk_info_index
].device_label
,
276 kTestDisks
[disk_info_index
].drive_label
,
277 kTestDisks
[disk_info_index
].vendor_id
,
278 kTestDisks
[disk_info_index
].vendor_name
,
279 kTestDisks
[disk_info_index
].product_id
,
280 kTestDisks
[disk_info_index
].product_name
,
281 kTestDisks
[disk_info_index
].fs_uuid
,
282 kTestDisks
[disk_info_index
].system_path_prefix
,
283 kTestDisks
[disk_info_index
].device_type
,
284 kTestDisks
[disk_info_index
].size_in_bytes
,
285 kTestDisks
[disk_info_index
].is_parent
,
286 kTestDisks
[disk_info_index
].is_read_only
,
287 kTestDisks
[disk_info_index
].has_media
,
288 kTestDisks
[disk_info_index
].on_boot_device
,
289 kTestDisks
[disk_info_index
].on_removable_device
,
290 kTestDisks
[disk_info_index
].is_hidden
297 const DiskMountManager::Disk
* FindVolumeBySourcePath(
298 const std::string
& source_path
) {
299 DiskMountManager::DiskMap::const_iterator volume_it
=
300 volumes_
.find(source_path
);
301 return (volume_it
== volumes_
.end()) ? NULL
: volume_it
->second
;
305 chromeos::disks::MockDiskMountManager
* disk_mount_manager_mock_
;
306 DiskMountManager::DiskMap volumes_
;
307 DiskMountManager::MountPointMap mount_points_
;
308 scoped_ptr
<TestingProfile
> testing_profile_
;
309 scoped_ptr
<file_manager::EventRouter
> event_router_
;
312 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest
, Mount
) {
313 file_manager::test_util::WaitUntilDriveMountPointIsAdded(
314 browser()->profile());
316 // Add a provided file system, to test passing the |configurable| and
317 // |source| flags properly down to Files app.
318 chromeos::file_system_provider::ProvidedFileSystemInfo
info(
319 "testing-extension-id", chromeos::file_system_provider::MountOptions(),
320 base::FilePath(), true /* configurable */, false /* watchable */,
321 extensions::SOURCE_NETWORK
);
323 file_manager::VolumeManager::Get(browser()->profile())
324 ->AddVolumeForTesting(
325 make_linked_ptr(file_manager::Volume::CreateForProvidedFileSystem(
326 info
, file_manager::MOUNT_CONTEXT_AUTO
)));
328 // We will call fileManagerPrivate.unmountVolume once. To test that method, we
329 // check that UnmountPath is really called with the same value.
330 EXPECT_CALL(*disk_mount_manager_mock_
, UnmountPath(_
, _
, _
))
332 EXPECT_CALL(*disk_mount_manager_mock_
,
334 chromeos::CrosDisksClient::GetArchiveMountPoint().AppendASCII(
335 "archive_mount_path").AsUTF8Unsafe(),
336 chromeos::UNMOUNT_OPTIONS_NONE
, _
)).Times(1);
338 ASSERT_TRUE(RunComponentExtensionTest("file_browser/mount_test"))
342 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest
, Permissions
) {
344 RunExtensionTestIgnoreManifestWarnings("file_browser/permissions"));
345 const extensions::Extension
* extension
= GetSingleLoadedExtension();
346 ASSERT_TRUE(extension
);
347 ASSERT_EQ(1u, extension
->install_warnings().size());
348 const extensions::InstallWarning
& warning
= extension
->install_warnings()[0];
349 EXPECT_EQ("fileManagerPrivate", warning
.key
);
352 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest
, OnFileChanged
) {
353 // In drive volume, deletion of a directory is notified via OnFileChanged.
354 // Local changes directly come to HandleFileWatchNotification from
356 typedef drive::FileChange FileChange
;
357 typedef drive::FileChange::FileType FileType
;
358 typedef drive::FileChange::ChangeType ChangeType
;
361 event_router_
->SetDispatchDirectoryChangeEventImplForTesting(
362 base::Bind(&DispatchDirectoryChangeEventImpl
, &counter
));
364 // /a/b/c and /a/d/e are being watched.
365 event_router_
->AddFileWatch(
366 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/a/b/c")),
367 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs-virtual/root/a/b/c")),
368 "extension_1", base::Bind(&AddFileWatchCallback
));
370 event_router_
->AddFileWatch(
371 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/a/d/e")),
372 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs-hash/root/a/d/e")),
373 "extension_2", base::Bind(&AddFileWatchCallback
));
375 event_router_
->AddFileWatch(
376 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/aaa")),
377 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs-hash/root/aaa")),
378 "extension_3", base::Bind(&AddFileWatchCallback
));
380 // event_router->addFileWatch create some tasks which are performed on message
381 // loop of BrowserThread::FILE. Wait until they are done.
382 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
383 // We also wait the UI thread here, since some tasks which are performed above
384 // message loop back results to the UI thread.
385 base::RunLoop().RunUntilIdle();
387 // When /a is deleted (1 and 2 is notified).
388 FileChange first_change
;
390 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/a")),
391 FileType::FILE_TYPE_DIRECTORY
, ChangeType::CHANGE_TYPE_DELETE
);
392 event_router_
->OnFileChanged(first_change
);
393 EXPECT_EQ(2, counter
);
395 // When /a/b/c is deleted (1 is notified).
396 FileChange second_change
;
397 second_change
.Update(
398 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/a/b/c")),
399 FileType::FILE_TYPE_DIRECTORY
, ChangeType::CHANGE_TYPE_DELETE
);
400 event_router_
->OnFileChanged(second_change
);
401 EXPECT_EQ(3, counter
);
403 // When /z/y is deleted (Not notified).
404 FileChange third_change
;
406 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/z/y")),
407 FileType::FILE_TYPE_DIRECTORY
, ChangeType::CHANGE_TYPE_DELETE
);
408 event_router_
->OnFileChanged(third_change
);
409 EXPECT_EQ(3, counter
);
411 // Remove file watchers.
412 event_router_
->RemoveFileWatch(
413 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/a/b/c")),
415 event_router_
->RemoveFileWatch(
416 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/a/d/e")),
418 event_router_
->RemoveFileWatch(
419 base::FilePath(FILE_PATH_LITERAL("/no-existing-fs/root/aaa")),
422 // event_router->removeFileWatch create some tasks which are performed on
423 // message loop of BrowserThread::FILE. Wait until they are done.
424 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);
427 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest
, ContentChecksum
) {
428 base::ScopedTempDir temp_dir
;
429 base::FilePath mount_point_dir
;
430 const char kLocalMountPointName
[] = "local";
432 ASSERT_TRUE(InitializeLocalFileSystem(kLocalMountPointName
, &temp_dir
,
434 << "Failed to initialize test file system";
436 EXPECT_TRUE(content::BrowserContext::GetMountPoints(browser()->profile())
437 ->RegisterFileSystem(
438 kLocalMountPointName
, storage::kFileSystemTypeNativeLocal
,
439 storage::FileSystemMountOption(), mount_point_dir
));
440 file_manager::VolumeManager::Get(browser()->profile())
441 ->AddVolumeForTesting(mount_point_dir
, file_manager::VOLUME_TYPE_TESTING
,
442 chromeos::DEVICE_TYPE_UNKNOWN
,
443 false /* read_only */);
445 ASSERT_TRUE(RunComponentExtensionTest("file_browser/content_checksum_test"));