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 <string> 6#include <vector> 7 8#include "base/files/file.h" 9#include "base/memory/ref_counted.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/strings/string_number_conversions.h" 12#include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h" 13#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" 14#include "chrome/browser/chromeos/file_system_provider/observer.h" 15#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h" 16#include "chrome/browser/chromeos/file_system_provider/service.h" 17#include "chrome/browser/chromeos/login/users/fake_user_manager.h" 18#include "chrome/common/pref_names.h" 19#include "chrome/test/base/testing_pref_service_syncable.h" 20#include "chrome/test/base/testing_profile.h" 21#include "components/user_prefs/user_prefs.h" 22#include "content/public/test/test_browser_thread_bundle.h" 23#include "extensions/browser/extension_registry.h" 24#include "extensions/common/extension.h" 25#include "extensions/common/manifest_constants.h" 26#include "testing/gtest/include/gtest/gtest.h" 27#include "webkit/browser/fileapi/external_mount_points.h" 28 29namespace chromeos { 30namespace file_system_provider { 31namespace { 32 33const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj"; 34const char kFileSystemId[] = "camera/pictures/id !@#$%^&*()_+"; 35const char kFileSystemName[] = "Camera Pictures"; 36 37// Utility observer, logging events from file_system_provider::Service. 38class LoggingObserver : public Observer { 39 public: 40 class Event { 41 public: 42 Event(const ProvidedFileSystemInfo& file_system_info, 43 base::File::Error error) 44 : file_system_info_(file_system_info), error_(error) {} 45 ~Event() {} 46 47 const ProvidedFileSystemInfo& file_system_info() { 48 return file_system_info_; 49 } 50 base::File::Error error() { return error_; } 51 52 private: 53 ProvidedFileSystemInfo file_system_info_; 54 base::File::Error error_; 55 }; 56 57 LoggingObserver() {} 58 virtual ~LoggingObserver() {} 59 60 // file_system_provider::Observer overrides. 61 virtual void OnProvidedFileSystemMount( 62 const ProvidedFileSystemInfo& file_system_info, 63 base::File::Error error) OVERRIDE { 64 mounts.push_back(Event(file_system_info, error)); 65 } 66 67 virtual void OnProvidedFileSystemUnmount( 68 const ProvidedFileSystemInfo& file_system_info, 69 base::File::Error error) OVERRIDE { 70 unmounts.push_back(Event(file_system_info, error)); 71 } 72 73 std::vector<Event> mounts; 74 std::vector<Event> unmounts; 75}; 76 77// Creates a fake extension with the specified |extension_id|. 78scoped_refptr<extensions::Extension> createFakeExtension( 79 const std::string& extension_id) { 80 base::DictionaryValue manifest; 81 std::string error; 82 manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0"); 83 manifest.SetString(extensions::manifest_keys::kName, "unused"); 84 return extensions::Extension::Create(base::FilePath(), 85 extensions::Manifest::UNPACKED, 86 manifest, 87 extensions::Extension::NO_FLAGS, 88 extension_id, 89 &error); 90} 91 92// Stores a provided file system information in preferences. 93void RememberFakeFileSystem(TestingProfile* profile, 94 const std::string& extension_id, 95 const std::string& file_system_id, 96 const std::string& file_system_name) { 97 TestingPrefServiceSyncable* pref_service = profile->GetTestingPrefService(); 98 ASSERT_TRUE(pref_service); 99 100 base::DictionaryValue extensions; 101 base::ListValue* file_systems = new base::ListValue(); 102 base::DictionaryValue* file_system = new base::DictionaryValue(); 103 file_system->SetString(kPrefKeyFileSystemId, kFileSystemId); 104 file_system->SetString(kPrefKeyFileSystemName, kFileSystemName); 105 file_systems->Append(file_system); 106 extensions.Set(kExtensionId, file_systems); 107 108 pref_service->Set(prefs::kFileSystemProviderMounted, extensions); 109} 110 111} // namespace 112 113class FileSystemProviderServiceTest : public testing::Test { 114 protected: 115 FileSystemProviderServiceTest() {} 116 virtual ~FileSystemProviderServiceTest() {} 117 118 virtual void SetUp() OVERRIDE { 119 profile_.reset(new TestingProfile); 120 user_manager_ = new FakeUserManager(); 121 user_manager_->AddUser(profile_->GetProfileName()); 122 user_manager_enabler_.reset(new ScopedUserManagerEnabler(user_manager_)); 123 extension_registry_.reset( 124 new extensions::ExtensionRegistry(profile_.get())); 125 file_system_provider_service_.reset( 126 new Service(profile_.get(), extension_registry_.get())); 127 file_system_provider_service_->SetFileSystemFactoryForTesting( 128 base::Bind(&FakeProvidedFileSystem::Create)); 129 extension_ = createFakeExtension(kExtensionId); 130 } 131 132 content::TestBrowserThreadBundle thread_bundle_; 133 scoped_ptr<TestingProfile> profile_; 134 FakeUserManager* user_manager_; 135 scoped_ptr<ScopedUserManagerEnabler> user_manager_enabler_; 136 scoped_ptr<extensions::ExtensionRegistry> extension_registry_; 137 scoped_ptr<Service> file_system_provider_service_; 138 scoped_refptr<extensions::Extension> extension_; 139}; 140 141TEST_F(FileSystemProviderServiceTest, MountFileSystem) { 142 LoggingObserver observer; 143 file_system_provider_service_->AddObserver(&observer); 144 145 const bool result = file_system_provider_service_->MountFileSystem( 146 kExtensionId, kFileSystemId, kFileSystemName); 147 EXPECT_TRUE(result); 148 149 ASSERT_EQ(1u, observer.mounts.size()); 150 EXPECT_EQ(kExtensionId, observer.mounts[0].file_system_info().extension_id()); 151 EXPECT_EQ(kFileSystemId, 152 observer.mounts[0].file_system_info().file_system_id()); 153 base::FilePath expected_mount_path = 154 util::GetMountPath(profile_.get(), kExtensionId, kFileSystemId); 155 EXPECT_EQ(expected_mount_path.AsUTF8Unsafe(), 156 observer.mounts[0].file_system_info().mount_path().AsUTF8Unsafe()); 157 EXPECT_EQ(kFileSystemName, 158 observer.mounts[0].file_system_info().file_system_name()); 159 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 160 ASSERT_EQ(0u, observer.unmounts.size()); 161 162 std::vector<ProvidedFileSystemInfo> file_system_info_list = 163 file_system_provider_service_->GetProvidedFileSystemInfoList(); 164 ASSERT_EQ(1u, file_system_info_list.size()); 165 166 file_system_provider_service_->RemoveObserver(&observer); 167} 168 169TEST_F(FileSystemProviderServiceTest, MountFileSystem_UniqueIds) { 170 LoggingObserver observer; 171 file_system_provider_service_->AddObserver(&observer); 172 173 const bool result = file_system_provider_service_->MountFileSystem( 174 kExtensionId, kFileSystemId, kFileSystemName); 175 EXPECT_TRUE(result); 176 177 const bool second_result = file_system_provider_service_->MountFileSystem( 178 kExtensionId, kFileSystemId, kFileSystemName); 179 EXPECT_FALSE(second_result); 180 181 ASSERT_EQ(2u, observer.mounts.size()); 182 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 183 EXPECT_EQ(base::File::FILE_ERROR_EXISTS, observer.mounts[1].error()); 184 185 std::vector<ProvidedFileSystemInfo> file_system_info_list = 186 file_system_provider_service_->GetProvidedFileSystemInfoList(); 187 ASSERT_EQ(1u, file_system_info_list.size()); 188 189 file_system_provider_service_->RemoveObserver(&observer); 190} 191 192TEST_F(FileSystemProviderServiceTest, MountFileSystem_StressTest) { 193 LoggingObserver observer; 194 file_system_provider_service_->AddObserver(&observer); 195 196 const size_t kMaxFileSystems = 16; 197 for (size_t i = 0; i < kMaxFileSystems; ++i) { 198 const std::string file_system_id = 199 std::string("test-") + base::IntToString(i); 200 const bool result = file_system_provider_service_->MountFileSystem( 201 kExtensionId, file_system_id, kFileSystemName); 202 EXPECT_TRUE(result); 203 } 204 ASSERT_EQ(kMaxFileSystems, observer.mounts.size()); 205 206 // The next file system is out of limit, and registering it should fail. 207 const bool result = file_system_provider_service_->MountFileSystem( 208 kExtensionId, kFileSystemId, kFileSystemName); 209 EXPECT_FALSE(result); 210 211 ASSERT_EQ(kMaxFileSystems + 1, observer.mounts.size()); 212 EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, 213 observer.mounts[kMaxFileSystems].error()); 214 215 std::vector<ProvidedFileSystemInfo> file_system_info_list = 216 file_system_provider_service_->GetProvidedFileSystemInfoList(); 217 ASSERT_EQ(kMaxFileSystems, file_system_info_list.size()); 218 219 file_system_provider_service_->RemoveObserver(&observer); 220} 221 222TEST_F(FileSystemProviderServiceTest, UnmountFileSystem) { 223 LoggingObserver observer; 224 file_system_provider_service_->AddObserver(&observer); 225 226 const bool result = file_system_provider_service_->MountFileSystem( 227 kExtensionId, kFileSystemId, kFileSystemName); 228 EXPECT_TRUE(result); 229 ASSERT_EQ(1u, observer.mounts.size()); 230 231 const bool unmount_result = file_system_provider_service_->UnmountFileSystem( 232 kExtensionId, kFileSystemId); 233 EXPECT_TRUE(unmount_result); 234 ASSERT_EQ(1u, observer.unmounts.size()); 235 EXPECT_EQ(base::File::FILE_OK, observer.unmounts[0].error()); 236 237 EXPECT_EQ(kExtensionId, 238 observer.unmounts[0].file_system_info().extension_id()); 239 EXPECT_EQ(kFileSystemId, 240 observer.unmounts[0].file_system_info().file_system_id()); 241 242 std::vector<ProvidedFileSystemInfo> file_system_info_list = 243 file_system_provider_service_->GetProvidedFileSystemInfoList(); 244 ASSERT_EQ(0u, file_system_info_list.size()); 245 246 file_system_provider_service_->RemoveObserver(&observer); 247} 248 249TEST_F(FileSystemProviderServiceTest, UnmountFileSystem_OnExtensionUnload) { 250 LoggingObserver observer; 251 file_system_provider_service_->AddObserver(&observer); 252 253 const bool result = file_system_provider_service_->MountFileSystem( 254 kExtensionId, kFileSystemId, kFileSystemName); 255 EXPECT_TRUE(result); 256 ASSERT_EQ(1u, observer.mounts.size()); 257 258 // Directly call the observer's method. 259 file_system_provider_service_->OnExtensionUnloaded( 260 profile_.get(), 261 extension_.get(), 262 extensions::UnloadedExtensionInfo::REASON_DISABLE); 263 264 ASSERT_EQ(1u, observer.unmounts.size()); 265 EXPECT_EQ(base::File::FILE_OK, observer.unmounts[0].error()); 266 267 EXPECT_EQ(kExtensionId, 268 observer.unmounts[0].file_system_info().extension_id()); 269 EXPECT_EQ(kFileSystemId, 270 observer.unmounts[0].file_system_info().file_system_id()); 271 272 std::vector<ProvidedFileSystemInfo> file_system_info_list = 273 file_system_provider_service_->GetProvidedFileSystemInfoList(); 274 ASSERT_EQ(0u, file_system_info_list.size()); 275 276 file_system_provider_service_->RemoveObserver(&observer); 277} 278 279TEST_F(FileSystemProviderServiceTest, UnmountFileSystem_WrongExtensionId) { 280 LoggingObserver observer; 281 file_system_provider_service_->AddObserver(&observer); 282 283 const std::string kWrongExtensionId = "helloworldhelloworldhelloworldhe"; 284 285 const bool result = file_system_provider_service_->MountFileSystem( 286 kExtensionId, kFileSystemId, kFileSystemName); 287 EXPECT_TRUE(result); 288 ASSERT_EQ(1u, observer.mounts.size()); 289 ASSERT_EQ( 290 1u, 291 file_system_provider_service_->GetProvidedFileSystemInfoList().size()); 292 293 const bool unmount_result = file_system_provider_service_->UnmountFileSystem( 294 kWrongExtensionId, kFileSystemId); 295 EXPECT_FALSE(unmount_result); 296 ASSERT_EQ(1u, observer.unmounts.size()); 297 EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, observer.unmounts[0].error()); 298 ASSERT_EQ( 299 1u, 300 file_system_provider_service_->GetProvidedFileSystemInfoList().size()); 301 302 std::vector<ProvidedFileSystemInfo> file_system_info_list = 303 file_system_provider_service_->GetProvidedFileSystemInfoList(); 304 ASSERT_EQ(1u, file_system_info_list.size()); 305 306 file_system_provider_service_->RemoveObserver(&observer); 307} 308 309TEST_F(FileSystemProviderServiceTest, RestoreFileSystem_OnExtensionLoad) { 310 LoggingObserver observer; 311 file_system_provider_service_->AddObserver(&observer); 312 313 // Create a fake entry in the preferences. 314 RememberFakeFileSystem( 315 profile_.get(), kExtensionId, kFileSystemId, kFileSystemName); 316 317 EXPECT_EQ(0u, observer.mounts.size()); 318 319 // Directly call the observer's method. 320 file_system_provider_service_->OnExtensionLoaded(profile_.get(), 321 extension_.get()); 322 323 ASSERT_EQ(1u, observer.mounts.size()); 324 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 325 326 EXPECT_EQ(kExtensionId, observer.mounts[0].file_system_info().extension_id()); 327 EXPECT_EQ(kFileSystemId, 328 observer.mounts[0].file_system_info().file_system_id()); 329 330 std::vector<ProvidedFileSystemInfo> file_system_info_list = 331 file_system_provider_service_->GetProvidedFileSystemInfoList(); 332 ASSERT_EQ(1u, file_system_info_list.size()); 333 334 file_system_provider_service_->RemoveObserver(&observer); 335} 336 337TEST_F(FileSystemProviderServiceTest, ForgetFileSystem_OnExtensionUnload) { 338 LoggingObserver observer; 339 file_system_provider_service_->AddObserver(&observer); 340 341 // Create a fake entry in the preferences. 342 RememberFakeFileSystem( 343 profile_.get(), kExtensionId, kFileSystemId, kFileSystemName); 344 345 // Directly call the observer's methods. 346 file_system_provider_service_->OnExtensionLoaded(profile_.get(), 347 extension_.get()); 348 349 file_system_provider_service_->OnExtensionUnloaded( 350 profile_.get(), 351 extension_.get(), 352 extensions::UnloadedExtensionInfo::REASON_DISABLE); 353 354 ASSERT_EQ(1u, observer.mounts.size()); 355 EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error()); 356 ASSERT_EQ(1u, observer.unmounts.size()); 357 EXPECT_EQ(base::File::FILE_OK, observer.unmounts[0].error()); 358 359 TestingPrefServiceSyncable* pref_service = profile_->GetTestingPrefService(); 360 ASSERT_TRUE(pref_service); 361 362 const base::DictionaryValue* extensions = 363 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 364 ASSERT_TRUE(extensions); 365 366 const base::ListValue* file_systems; 367 EXPECT_FALSE(extensions->GetList(kExtensionId, &file_systems)); 368 369 file_system_provider_service_->RemoveObserver(&observer); 370} 371 372TEST_F(FileSystemProviderServiceTest, RememberFileSystem_OnShutdown) { 373 { 374 scoped_ptr<Service> service( 375 new Service(profile_.get(), extension_registry_.get())); 376 service->SetFileSystemFactoryForTesting( 377 base::Bind(&FakeProvidedFileSystem::Create)); 378 379 LoggingObserver observer; 380 service->AddObserver(&observer); 381 382 const bool result = 383 service->MountFileSystem(kExtensionId, kFileSystemId, kFileSystemName); 384 EXPECT_TRUE(result); 385 ASSERT_EQ(1u, observer.mounts.size()); 386 387 service->RemoveObserver(&observer); 388 } 389 390 TestingPrefServiceSyncable* pref_service = profile_->GetTestingPrefService(); 391 ASSERT_TRUE(pref_service); 392 393 const base::DictionaryValue* extensions = 394 pref_service->GetDictionary(prefs::kFileSystemProviderMounted); 395 ASSERT_TRUE(extensions); 396 397 const base::ListValue* file_systems; 398 ASSERT_TRUE(extensions->GetList(kExtensionId, &file_systems)); 399 ASSERT_EQ(1u, file_systems->GetSize()); 400 401 const base::DictionaryValue* file_system = NULL; 402 ASSERT_TRUE(file_systems->GetDictionary(0, &file_system)); 403 404 std::string file_system_id; 405 file_system->GetString(kPrefKeyFileSystemId, &file_system_id); 406 EXPECT_EQ(kFileSystemId, file_system_id); 407 408 std::string file_system_name; 409 file_system->GetString(kPrefKeyFileSystemName, &file_system_name); 410 EXPECT_EQ(kFileSystemName, file_system_name); 411} 412 413} // namespace file_system_provider 414} // namespace chromeos 415