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. 4 5 #include "chrome/browser/chromeos/file_system_provider/service.h" 6 7 #include <string> 8 #include <vector> 9 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/login/users/fake_user_manager.h" 19 #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" 20 #include "chrome/common/pref_names.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" 32 33 namespace chromeos { 34 namespace file_system_provider { 35 namespace { 36 37 const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj"; 38 const char kDisplayName[] = "Camera Pictures"; 39 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 .!@#$%^&*()_+"; 44 45 // Utility observer, logging events from file_system_provider::Service. 46 class LoggingObserver : public Observer { 47 public: 48 class Event { 49 public: 50 Event(const ProvidedFileSystemInfo& file_system_info, 51 base::File::Error error) 52 : file_system_info_(file_system_info), error_(error) {} 53 ~Event() {} 54 55 const ProvidedFileSystemInfo& file_system_info() { 56 return file_system_info_; 57 } 58 base::File::Error error() { return error_; } 59 60 private: 61 ProvidedFileSystemInfo file_system_info_; 62 base::File::Error error_; 63 }; 64 65 LoggingObserver() {} 66 virtual ~LoggingObserver() {} 67 68 // file_system_provider::Observer overrides. 69 virtual void OnProvidedFileSystemMount( 70 const ProvidedFileSystemInfo& file_system_info, 71 base::File::Error error) OVERRIDE { 72 mounts.push_back(Event(file_system_info, error)); 73 } 74 75 virtual void OnProvidedFileSystemUnmount( 76 const ProvidedFileSystemInfo& file_system_info, 77 base::File::Error error) OVERRIDE { 78 unmounts.push_back(Event(file_system_info, error)); 79 } 80 81 std::vector<Event> mounts; 82 std::vector<Event> unmounts; 83 }; 84 85 // Creates a fake extension with the specified |extension_id|. 86 scoped_refptr<extensions::Extension> createFakeExtension( 87 const std::string& extension_id) { 88 base::DictionaryValue manifest; 89 std::string error; 90 manifest.SetStringWithoutPathExpansion(extensions::manifest_keys::kVersion, 91 "1.0.0.0"); 92 manifest.SetStringWithoutPathExpansion(extensions::manifest_keys::kName, 93 "unused"); 94 return extensions::Extension::Create(base::FilePath(), 95 extensions::Manifest::UNPACKED, 96 manifest, 97 extensions::Extension::NO_FLAGS, 98 extension_id, 99 &error); 100 } 101 102 // Stores a provided file system information in preferences. 103 void RememberFakeFileSystem(TestingProfile* profile, 104 const std::string& extension_id, 105 const std::string& file_system_id, 106 const std::string& display_name, 107 bool writable) { 108 TestingPrefServiceSyncable* const pref_service = 109 profile->GetTestingPrefService(); 110 ASSERT_TRUE(pref_service); 111 112 base::DictionaryValue extensions; 113 base::DictionaryValue* file_systems = new base::DictionaryValue(); 114 base::DictionaryValue* file_system = new base::DictionaryValue(); 115 file_system->SetStringWithoutPathExpansion(kPrefKeyFileSystemId, 116 kFileSystemId); 117 file_system->SetStringWithoutPathExpansion(kPrefKeyDisplayName, kDisplayName); 118 file_system->SetBooleanWithoutPathExpansion(kPrefKeyWritable, writable); 119 file_systems->SetWithoutPathExpansion(kFileSystemId, file_system); 120 extensions.SetWithoutPathExpansion(kExtensionId, file_systems); 121 122 pref_service->Set(prefs::kFileSystemProviderMounted, extensions); 123 } 124 125 } // namespace 126 127 class FileSystemProviderServiceTest : public testing::Test { 128 protected: 129 FileSystemProviderServiceTest() : profile_(NULL) {} 130 131 virtual ~FileSystemProviderServiceTest() {} 132 133 virtual void SetUp() OVERRIDE { 134 profile_manager_.reset( 135 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); 136 ASSERT_TRUE(profile_manager_->SetUp()); 137 profile_ = profile_manager_->CreateTestingProfile("test-user (at) example.com"); 138 user_manager_ = new FakeUserManager(); 139 user_manager_->AddUser(profile_->GetProfileName()); 140 user_manager_enabler_.reset(new ScopedUserManagerEnabler(user_manager_)); 141 extension_registry_.reset(new extensions::ExtensionRegistry(profile_)); 142 service_.reset(new Service(profile_, extension_registry_.get())); 143 service_->SetFileSystemFactoryForTesting( 144 base::Bind(&FakeProvidedFileSystem::Create)); 145 extension_ = createFakeExtension(kExtensionId); 146 } 147 148 content::TestBrowserThreadBundle thread_bundle_; 149 scoped_ptr<TestingProfileManager> profile_manager_; 150 TestingProfile* profile_; 151 FakeUserManager* user_manager_; 152 scoped_ptr<ScopedUserManagerEnabler> user_manager_enabler_; 153 scoped_ptr<extensions::ExtensionRegistry> extension_registry_; 154 scoped_ptr<Service> service_; 155 scoped_refptr<extensions::Extension> extension_; 156 }; 157 158 TEST_F(FileSystemProviderServiceTest, MountFileSystem) { 159 LoggingObserver observer; 160 service_->AddObserver(&observer); 161 162 EXPECT_TRUE(service_->MountFileSystem( 163 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 164 165 ASSERT_EQ(1u, observer.mounts.size()); 166 EXPECT_EQ(kExtensionId, observer.mounts[0].file_system_info().extension_id()); 167 EXPECT_EQ(kFileSystemId, 168 observer.mounts[0].file_system_info().file_system_id()); 169 base::FilePath expected_mount_path = 170 util::GetMountPath(profile_, kExtensionId, kFileSystemId); 171 EXPECT_EQ(expected_mount_path.AsUTF8Unsafe(), 172 observer.mounts[0].file_system_info().mount_path().AsUTF8Unsafe()); 173 EXPECT_EQ(kDisplayName, observer.mounts[0].file_system_info().display_name()); 174 EXPECT_FALSE(observer.mounts[0].file_system_info().writable()); 175 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 176 ASSERT_EQ(0u, observer.unmounts.size()); 177 178 std::vector<ProvidedFileSystemInfo> file_system_info_list = 179 service_->GetProvidedFileSystemInfoList(); 180 ASSERT_EQ(1u, file_system_info_list.size()); 181 182 service_->RemoveObserver(&observer); 183 } 184 185 TEST_F(FileSystemProviderServiceTest, MountFileSystem_Writable) { 186 LoggingObserver observer; 187 service_->AddObserver(&observer); 188 189 EXPECT_TRUE(service_->MountFileSystem( 190 kExtensionId, kFileSystemId, kDisplayName, true /* writable */)); 191 192 ASSERT_EQ(1u, observer.mounts.size()); 193 EXPECT_TRUE(observer.mounts[0].file_system_info().writable()); 194 ASSERT_EQ(0u, observer.unmounts.size()); 195 std::vector<ProvidedFileSystemInfo> file_system_info_list = 196 service_->GetProvidedFileSystemInfoList(); 197 ASSERT_EQ(1u, file_system_info_list.size()); 198 199 service_->RemoveObserver(&observer); 200 } 201 202 TEST_F(FileSystemProviderServiceTest, MountFileSystem_UniqueIds) { 203 LoggingObserver observer; 204 service_->AddObserver(&observer); 205 206 EXPECT_TRUE(service_->MountFileSystem( 207 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 208 EXPECT_FALSE(service_->MountFileSystem( 209 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 210 211 ASSERT_EQ(2u, observer.mounts.size()); 212 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 213 EXPECT_EQ(base::File::FILE_ERROR_EXISTS, observer.mounts[1].error()); 214 215 std::vector<ProvidedFileSystemInfo> file_system_info_list = 216 service_->GetProvidedFileSystemInfoList(); 217 ASSERT_EQ(1u, file_system_info_list.size()); 218 219 service_->RemoveObserver(&observer); 220 } 221 222 TEST_F(FileSystemProviderServiceTest, MountFileSystem_StressTest) { 223 LoggingObserver observer; 224 service_->AddObserver(&observer); 225 226 const size_t kMaxFileSystems = 16; 227 for (size_t i = 0; i < kMaxFileSystems; ++i) { 228 const std::string file_system_id = 229 std::string("test-") + base::IntToString(i); 230 EXPECT_TRUE(service_->MountFileSystem( 231 kExtensionId, file_system_id, kDisplayName, false /* writable */)); 232 } 233 ASSERT_EQ(kMaxFileSystems, observer.mounts.size()); 234 235 // The next file system is out of limit, and registering it should fail. 236 EXPECT_FALSE(service_->MountFileSystem( 237 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 238 239 ASSERT_EQ(kMaxFileSystems + 1, observer.mounts.size()); 240 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, 241 observer.mounts[kMaxFileSystems].error()); 242 243 std::vector<ProvidedFileSystemInfo> file_system_info_list = 244 service_->GetProvidedFileSystemInfoList(); 245 ASSERT_EQ(kMaxFileSystems, file_system_info_list.size()); 246 247 service_->RemoveObserver(&observer); 248 } 249 250 TEST_F(FileSystemProviderServiceTest, UnmountFileSystem) { 251 LoggingObserver observer; 252 service_->AddObserver(&observer); 253 254 EXPECT_TRUE(service_->MountFileSystem( 255 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 256 ASSERT_EQ(1u, observer.mounts.size()); 257 258 EXPECT_TRUE(service_->UnmountFileSystem( 259 kExtensionId, kFileSystemId, Service::UNMOUNT_REASON_USER)); 260 ASSERT_EQ(1u, observer.unmounts.size()); 261 EXPECT_EQ(base::File::FILE_OK, observer.unmounts[0].error()); 262 263 EXPECT_EQ(kExtensionId, 264 observer.unmounts[0].file_system_info().extension_id()); 265 EXPECT_EQ(kFileSystemId, 266 observer.unmounts[0].file_system_info().file_system_id()); 267 268 std::vector<ProvidedFileSystemInfo> file_system_info_list = 269 service_->GetProvidedFileSystemInfoList(); 270 ASSERT_EQ(0u, file_system_info_list.size()); 271 272 service_->RemoveObserver(&observer); 273 } 274 275 TEST_F(FileSystemProviderServiceTest, UnmountFileSystem_OnExtensionUnload) { 276 LoggingObserver observer; 277 service_->AddObserver(&observer); 278 279 EXPECT_TRUE(service_->MountFileSystem( 280 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 281 ASSERT_EQ(1u, observer.mounts.size()); 282 283 // Directly call the observer's method. 284 service_->OnExtensionUnloaded( 285 profile_, 286 extension_.get(), 287 extensions::UnloadedExtensionInfo::REASON_DISABLE); 288 289 ASSERT_EQ(1u, observer.unmounts.size()); 290 EXPECT_EQ(base::File::FILE_OK, observer.unmounts[0].error()); 291 292 EXPECT_EQ(kExtensionId, 293 observer.unmounts[0].file_system_info().extension_id()); 294 EXPECT_EQ(kFileSystemId, 295 observer.unmounts[0].file_system_info().file_system_id()); 296 297 std::vector<ProvidedFileSystemInfo> file_system_info_list = 298 service_->GetProvidedFileSystemInfoList(); 299 ASSERT_EQ(0u, file_system_info_list.size()); 300 301 service_->RemoveObserver(&observer); 302 } 303 304 TEST_F(FileSystemProviderServiceTest, UnmountFileSystem_WrongExtensionId) { 305 LoggingObserver observer; 306 service_->AddObserver(&observer); 307 308 const std::string kWrongExtensionId = "helloworldhelloworldhelloworldhe"; 309 310 EXPECT_TRUE(service_->MountFileSystem( 311 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 312 ASSERT_EQ(1u, observer.mounts.size()); 313 ASSERT_EQ(1u, service_->GetProvidedFileSystemInfoList().size()); 314 315 EXPECT_FALSE(service_->UnmountFileSystem( 316 kWrongExtensionId, kFileSystemId, Service::UNMOUNT_REASON_USER)); 317 ASSERT_EQ(1u, observer.unmounts.size()); 318 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, observer.unmounts[0].error()); 319 ASSERT_EQ(1u, service_->GetProvidedFileSystemInfoList().size()); 320 321 std::vector<ProvidedFileSystemInfo> file_system_info_list = 322 service_->GetProvidedFileSystemInfoList(); 323 ASSERT_EQ(1u, file_system_info_list.size()); 324 325 service_->RemoveObserver(&observer); 326 } 327 328 TEST_F(FileSystemProviderServiceTest, RestoreFileSystem_OnExtensionLoad) { 329 // Create a fake entry in the preferences. 330 RememberFakeFileSystem( 331 profile_, kExtensionId, kFileSystemId, kDisplayName, true /* writable */); 332 333 // Create a new service instance in order to load remembered file systems 334 // from preferences. 335 scoped_ptr<Service> new_service( 336 new Service(profile_, extension_registry_.get())); 337 LoggingObserver observer; 338 new_service->AddObserver(&observer); 339 340 new_service->SetFileSystemFactoryForTesting( 341 base::Bind(&FakeProvidedFileSystem::Create)); 342 343 EXPECT_EQ(0u, observer.mounts.size()); 344 345 // Directly call the observer's method. 346 new_service->OnExtensionLoaded(profile_, extension_.get()); 347 348 ASSERT_EQ(1u, observer.mounts.size()); 349 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 350 351 EXPECT_EQ(kExtensionId, observer.mounts[0].file_system_info().extension_id()); 352 EXPECT_EQ(kFileSystemId, 353 observer.mounts[0].file_system_info().file_system_id()); 354 EXPECT_TRUE(observer.mounts[0].file_system_info().writable()); 355 356 std::vector<ProvidedFileSystemInfo> file_system_info_list = 357 new_service->GetProvidedFileSystemInfoList(); 358 ASSERT_EQ(1u, file_system_info_list.size()); 359 360 new_service->RemoveObserver(&observer); 361 } 362 363 TEST_F(FileSystemProviderServiceTest, RememberFileSystem_OnMount) { 364 LoggingObserver observer; 365 service_->AddObserver(&observer); 366 367 EXPECT_TRUE(service_->MountFileSystem( 368 kExtensionId, kFileSystemId, kDisplayName, true /* writable */)); 369 ASSERT_EQ(1u, observer.mounts.size()); 370 371 TestingPrefServiceSyncable* const pref_service = 372 profile_->GetTestingPrefService(); 373 ASSERT_TRUE(pref_service); 374 375 const base::DictionaryValue* const extensions = 376 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 377 ASSERT_TRUE(extensions); 378 379 const base::DictionaryValue* file_systems = NULL; 380 ASSERT_TRUE(extensions->GetDictionaryWithoutPathExpansion(kExtensionId, 381 &file_systems)); 382 EXPECT_EQ(1u, file_systems->size()); 383 384 const base::Value* file_system_value = NULL; 385 const base::DictionaryValue* file_system = NULL; 386 ASSERT_TRUE( 387 file_systems->GetWithoutPathExpansion(kFileSystemId, &file_system_value)); 388 ASSERT_TRUE(file_system_value->GetAsDictionary(&file_system)); 389 390 std::string file_system_id; 391 EXPECT_TRUE(file_system->GetStringWithoutPathExpansion(kPrefKeyFileSystemId, 392 &file_system_id)); 393 EXPECT_EQ(kFileSystemId, file_system_id); 394 395 std::string display_name; 396 EXPECT_TRUE(file_system->GetStringWithoutPathExpansion(kPrefKeyDisplayName, 397 &display_name)); 398 EXPECT_EQ(kDisplayName, display_name); 399 400 bool writable = false; 401 EXPECT_TRUE( 402 file_system->GetBooleanWithoutPathExpansion(kPrefKeyWritable, &writable)); 403 EXPECT_TRUE(writable); 404 405 service_->RemoveObserver(&observer); 406 } 407 408 TEST_F(FileSystemProviderServiceTest, RememberFileSystem_OnUnmountOnShutdown) { 409 LoggingObserver observer; 410 service_->AddObserver(&observer); 411 412 TestingPrefServiceSyncable* const pref_service = 413 profile_->GetTestingPrefService(); 414 ASSERT_TRUE(pref_service); 415 416 { 417 EXPECT_TRUE(service_->MountFileSystem( 418 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 419 ASSERT_EQ(1u, observer.mounts.size()); 420 421 const base::DictionaryValue* extensions = 422 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 423 ASSERT_TRUE(extensions); 424 425 const base::DictionaryValue* file_systems = NULL; 426 ASSERT_TRUE(extensions->GetDictionaryWithoutPathExpansion(kExtensionId, 427 &file_systems)); 428 EXPECT_EQ(1u, file_systems->size()); 429 } 430 431 { 432 EXPECT_TRUE(service_->UnmountFileSystem( 433 kExtensionId, kFileSystemId, Service::UNMOUNT_REASON_SHUTDOWN)); 434 435 const base::DictionaryValue* const extensions = 436 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 437 ASSERT_TRUE(extensions); 438 439 const base::DictionaryValue* file_systems = NULL; 440 ASSERT_TRUE(extensions->GetDictionaryWithoutPathExpansion(kExtensionId, 441 &file_systems)); 442 EXPECT_EQ(1u, file_systems->size()); 443 } 444 445 service_->RemoveObserver(&observer); 446 } 447 448 TEST_F(FileSystemProviderServiceTest, RememberFileSystem_OnUnmountByUser) { 449 LoggingObserver observer; 450 service_->AddObserver(&observer); 451 452 TestingPrefServiceSyncable* const pref_service = 453 profile_->GetTestingPrefService(); 454 ASSERT_TRUE(pref_service); 455 456 { 457 EXPECT_TRUE(service_->MountFileSystem( 458 kExtensionId, kFileSystemId, kDisplayName, false /* writable */)); 459 ASSERT_EQ(1u, observer.mounts.size()); 460 461 const base::DictionaryValue* extensions = 462 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 463 ASSERT_TRUE(extensions); 464 465 const base::DictionaryValue* file_systems = NULL; 466 ASSERT_TRUE(extensions->GetDictionaryWithoutPathExpansion(kExtensionId, 467 &file_systems)); 468 EXPECT_EQ(1u, file_systems->size()); 469 } 470 471 { 472 EXPECT_TRUE(service_->UnmountFileSystem( 473 kExtensionId, kFileSystemId, Service::UNMOUNT_REASON_USER)); 474 475 const base::DictionaryValue* const extensions = 476 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 477 ASSERT_TRUE(extensions); 478 479 const base::DictionaryValue* file_systems = NULL; 480 EXPECT_FALSE(extensions->GetDictionaryWithoutPathExpansion(kExtensionId, 481 &file_systems)); 482 } 483 484 service_->RemoveObserver(&observer); 485 } 486 487 } // namespace file_system_provider 488 } // namespace chromeos 489