Home | History | Annotate | Download | only in file_system_provider
      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