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/registry.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 #include "base/files/file_path.h"
13 #include "chrome/browser/chromeos/file_system_provider/icon_set.h"
14 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
15 #include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/testing_browser_process.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "chrome/test/base/testing_profile_manager.h"
20 #include "components/sync_preferences/testing_pref_service_syncable.h"
21 #include "components/user_prefs/user_prefs.h"
22 #include "content/public/test/browser_task_environment.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 
25 namespace chromeos {
26 namespace file_system_provider {
27 namespace {
28 
29 const char kTemporaryOrigin[] =
30     "chrome-extension://abcabcabcabcabcabcabcabcabcabcabcabca/";
31 const char kPersistentOrigin[] =
32     "chrome-extension://efgefgefgefgefgefgefgefgefgefgefgefge/";
33 const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
34 const char kDisplayName[] = "Camera Pictures";
35 const ProviderId kProviderId = ProviderId::CreateFromExtensionId(kExtensionId);
36 
37 // The dot in the file system ID is there in order to check that saving to
38 // preferences works correctly. File System ID is used as a key in
39 // a base::DictionaryValue, so it has to be stored without path expansion.
40 const char kFileSystemId[] = "camera/pictures/id .!@#$%^&*()_+";
41 
42 const int kOpenedFilesLimit = 5;
43 
44 // Stores a provided file system information in preferences together with a
45 // fake watcher.
RememberFakeFileSystem(TestingProfile * profile,const ProviderId & provider_id,const std::string & file_system_id,const std::string & display_name,bool writable,bool supports_notify_tag,int opened_files_limit,const Watcher & watcher)46 void RememberFakeFileSystem(TestingProfile* profile,
47                             const ProviderId& provider_id,
48                             const std::string& file_system_id,
49                             const std::string& display_name,
50                             bool writable,
51                             bool supports_notify_tag,
52                             int opened_files_limit,
53                             const Watcher& watcher) {
54   // Warning. Updating this code means that backward compatibility may be
55   // broken, what is unexpected and should be avoided.
56   sync_preferences::TestingPrefServiceSyncable* const pref_service =
57       profile->GetTestingPrefService();
58   ASSERT_TRUE(pref_service);
59 
60   base::DictionaryValue extensions;
61   auto file_system = std::make_unique<base::DictionaryValue>();
62   file_system->SetKey(kPrefKeyFileSystemId, base::Value(kFileSystemId));
63   file_system->SetKey(kPrefKeyDisplayName, base::Value(kDisplayName));
64   file_system->SetKey(kPrefKeyWritable, base::Value(writable));
65   file_system->SetKey(kPrefKeySupportsNotifyTag,
66                       base::Value(supports_notify_tag));
67   file_system->SetKey(kPrefKeyOpenedFilesLimit,
68                       base::Value(opened_files_limit));
69 
70   // Remember watchers.
71   auto watcher_value = std::make_unique<base::DictionaryValue>();
72   watcher_value->SetKey(kPrefKeyWatcherEntryPath,
73                         base::Value(watcher.entry_path.value()));
74   watcher_value->SetKey(kPrefKeyWatcherRecursive,
75                         base::Value(watcher.recursive));
76   watcher_value->SetKey(kPrefKeyWatcherLastTag, base::Value(watcher.last_tag));
77   auto persistent_origins_value = std::make_unique<base::ListValue>();
78   for (const auto& subscriber_it : watcher.subscribers) {
79     if (subscriber_it.second.persistent)
80       persistent_origins_value->AppendString(subscriber_it.first.spec());
81   }
82 
83   watcher_value->SetWithoutPathExpansion(kPrefKeyWatcherPersistentOrigins,
84                                          std::move(persistent_origins_value));
85   auto watchers = std::make_unique<base::DictionaryValue>();
86   watchers->SetWithoutPathExpansion(watcher.entry_path.value(),
87                                     std::move(watcher_value));
88   file_system->SetWithoutPathExpansion(kPrefKeyWatchers, std::move(watchers));
89   auto file_systems = std::make_unique<base::DictionaryValue>();
90   file_systems->SetWithoutPathExpansion(kFileSystemId, std::move(file_system));
91   extensions.SetWithoutPathExpansion(kProviderId.ToString(),
92                                      std::move(file_systems));
93   pref_service->Set(prefs::kFileSystemProviderMounted, extensions);
94 }
95 
96 }  // namespace
97 
98 class FileSystemProviderRegistryTest : public testing::Test {
99  protected:
FileSystemProviderRegistryTest()100   FileSystemProviderRegistryTest() : profile_(NULL) {}
101 
~FileSystemProviderRegistryTest()102   ~FileSystemProviderRegistryTest() override {}
103 
SetUp()104   void SetUp() override {
105     profile_manager_.reset(
106         new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
107     ASSERT_TRUE(profile_manager_->SetUp());
108     profile_ = profile_manager_->CreateTestingProfile("test-user@example.com");
109     registry_.reset(new Registry(profile_));
110     fake_watcher_.entry_path = base::FilePath(FILE_PATH_LITERAL("/a/b/c"));
111     fake_watcher_.recursive = true;
112     fake_watcher_.subscribers[GURL(kTemporaryOrigin)].origin =
113         GURL(kTemporaryOrigin);
114     fake_watcher_.subscribers[GURL(kTemporaryOrigin)].persistent = false;
115     fake_watcher_.subscribers[GURL(kPersistentOrigin)].origin =
116         GURL(kPersistentOrigin);
117     fake_watcher_.subscribers[GURL(kPersistentOrigin)].persistent = true;
118     fake_watcher_.last_tag = "hello-world";
119   }
120 
121   content::BrowserTaskEnvironment task_environment_;
122   std::unique_ptr<TestingProfileManager> profile_manager_;
123   TestingProfile* profile_;
124   std::unique_ptr<RegistryInterface> registry_;
125   Watcher fake_watcher_;
126 };
127 
TEST_F(FileSystemProviderRegistryTest,RestoreFileSystems)128 TEST_F(FileSystemProviderRegistryTest, RestoreFileSystems) {
129   // Create a fake entry in the preferences.
130   RememberFakeFileSystem(profile_, kProviderId, kFileSystemId, kDisplayName,
131                          true /* writable */, true /* supports_notify_tag */,
132                          kOpenedFilesLimit, fake_watcher_);
133 
134   std::unique_ptr<RegistryInterface::RestoredFileSystems>
135       restored_file_systems = registry_->RestoreFileSystems(kProviderId);
136 
137   ASSERT_EQ(1u, restored_file_systems->size());
138   const RegistryInterface::RestoredFileSystem& restored_file_system =
139       restored_file_systems->at(0);
140   EXPECT_EQ(kProviderId, restored_file_system.provider_id);
141   EXPECT_EQ(kFileSystemId, restored_file_system.options.file_system_id);
142   EXPECT_EQ(kDisplayName, restored_file_system.options.display_name);
143   EXPECT_TRUE(restored_file_system.options.writable);
144   EXPECT_TRUE(restored_file_system.options.supports_notify_tag);
145   EXPECT_EQ(kOpenedFilesLimit, restored_file_system.options.opened_files_limit);
146 
147   ASSERT_EQ(1u, restored_file_system.watchers.size());
148   const auto& restored_watcher_it = restored_file_system.watchers.find(
149       WatcherKey(fake_watcher_.entry_path, fake_watcher_.recursive));
150   ASSERT_NE(restored_file_system.watchers.end(), restored_watcher_it);
151 
152   EXPECT_EQ(fake_watcher_.entry_path, restored_watcher_it->second.entry_path);
153   EXPECT_EQ(fake_watcher_.recursive, restored_watcher_it->second.recursive);
154   EXPECT_EQ(fake_watcher_.last_tag, restored_watcher_it->second.last_tag);
155 }
156 
TEST_F(FileSystemProviderRegistryTest,RememberFileSystem)157 TEST_F(FileSystemProviderRegistryTest, RememberFileSystem) {
158   MountOptions options(kFileSystemId, kDisplayName);
159   options.writable = true;
160   options.supports_notify_tag = true;
161   options.opened_files_limit = kOpenedFilesLimit;
162 
163   ProvidedFileSystemInfo file_system_info(
164       kProviderId, options, base::FilePath(FILE_PATH_LITERAL("/a/b/c")),
165       false /* configurable */, true /* watchable */, extensions::SOURCE_FILE,
166       IconSet());
167 
168   Watchers watchers;
169   watchers[WatcherKey(fake_watcher_.entry_path, fake_watcher_.recursive)] =
170       fake_watcher_;
171 
172   registry_->RememberFileSystem(file_system_info, watchers);
173 
174   sync_preferences::TestingPrefServiceSyncable* const pref_service =
175       profile_->GetTestingPrefService();
176   ASSERT_TRUE(pref_service);
177 
178   const base::DictionaryValue* const extensions =
179       pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
180   ASSERT_TRUE(extensions);
181 
182   const base::DictionaryValue* file_systems = NULL;
183   ASSERT_TRUE(extensions->GetDictionaryWithoutPathExpansion(
184       kProviderId.ToString(), &file_systems));
185   EXPECT_EQ(1u, file_systems->size());
186 
187   const base::Value* file_system_value = NULL;
188   const base::DictionaryValue* file_system = NULL;
189   ASSERT_TRUE(
190       file_systems->GetWithoutPathExpansion(kFileSystemId, &file_system_value));
191   ASSERT_TRUE(file_system_value->GetAsDictionary(&file_system));
192 
193   std::string file_system_id;
194   EXPECT_TRUE(file_system->GetStringWithoutPathExpansion(kPrefKeyFileSystemId,
195                                                          &file_system_id));
196   EXPECT_EQ(kFileSystemId, file_system_id);
197 
198   std::string display_name;
199   EXPECT_TRUE(file_system->GetStringWithoutPathExpansion(kPrefKeyDisplayName,
200                                                          &display_name));
201   EXPECT_EQ(kDisplayName, display_name);
202 
203   bool writable = false;
204   EXPECT_TRUE(
205       file_system->GetBooleanWithoutPathExpansion(kPrefKeyWritable, &writable));
206   EXPECT_TRUE(writable);
207 
208   bool supports_notify_tag = false;
209   EXPECT_TRUE(file_system->GetBooleanWithoutPathExpansion(
210       kPrefKeySupportsNotifyTag, &supports_notify_tag));
211   EXPECT_TRUE(supports_notify_tag);
212 
213   int opened_files_limit = 0;
214   EXPECT_TRUE(file_system->GetIntegerWithoutPathExpansion(
215       kPrefKeyOpenedFilesLimit, &opened_files_limit));
216   EXPECT_EQ(kOpenedFilesLimit, opened_files_limit);
217 
218   const base::DictionaryValue* watchers_value = NULL;
219   ASSERT_TRUE(file_system->GetDictionaryWithoutPathExpansion(kPrefKeyWatchers,
220                                                              &watchers_value));
221 
222   const base::DictionaryValue* watcher = NULL;
223   ASSERT_TRUE(watchers_value->GetDictionaryWithoutPathExpansion(
224       fake_watcher_.entry_path.value(), &watcher));
225 
226   std::string entry_path;
227   EXPECT_TRUE(watcher->GetStringWithoutPathExpansion(kPrefKeyWatcherEntryPath,
228                                                      &entry_path));
229   EXPECT_EQ(fake_watcher_.entry_path.value(), entry_path);
230 
231   bool recursive = false;
232   EXPECT_TRUE(watcher->GetBooleanWithoutPathExpansion(kPrefKeyWatcherRecursive,
233                                                       &recursive));
234   EXPECT_EQ(fake_watcher_.recursive, recursive);
235 
236   std::string last_tag;
237   EXPECT_TRUE(watcher->GetStringWithoutPathExpansion(kPrefKeyWatcherLastTag,
238                                                      &last_tag));
239   EXPECT_EQ(fake_watcher_.last_tag, last_tag);
240 
241   const base::ListValue* persistent_origins = NULL;
242   ASSERT_TRUE(watcher->GetListWithoutPathExpansion(
243       kPrefKeyWatcherPersistentOrigins, &persistent_origins));
244   ASSERT_GT(fake_watcher_.subscribers.size(), persistent_origins->GetSize());
245   ASSERT_EQ(1u, persistent_origins->GetSize());
246   std::string persistent_origin;
247   EXPECT_TRUE(persistent_origins->GetString(0, &persistent_origin));
248   const auto& fake_subscriber_it =
249       fake_watcher_.subscribers.find(GURL(persistent_origin));
250   ASSERT_NE(fake_watcher_.subscribers.end(), fake_subscriber_it);
251   EXPECT_TRUE(fake_subscriber_it->second.persistent);
252 }
253 
TEST_F(FileSystemProviderRegistryTest,ForgetFileSystem)254 TEST_F(FileSystemProviderRegistryTest, ForgetFileSystem) {
255   // Create a fake file systems in the preferences.
256   RememberFakeFileSystem(profile_, kProviderId, kFileSystemId, kDisplayName,
257                          true /* writable */, true /* supports_notify_tag */,
258                          kOpenedFilesLimit, fake_watcher_);
259 
260   registry_->ForgetFileSystem(kProviderId, kFileSystemId);
261 
262   sync_preferences::TestingPrefServiceSyncable* const pref_service =
263       profile_->GetTestingPrefService();
264   ASSERT_TRUE(pref_service);
265 
266   const base::DictionaryValue* const extensions =
267       pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
268   ASSERT_TRUE(extensions);
269 
270   const base::DictionaryValue* file_systems = NULL;
271   EXPECT_FALSE(extensions->GetDictionaryWithoutPathExpansion(
272       kProviderId.GetExtensionId(), &file_systems));
273 }
274 
TEST_F(FileSystemProviderRegistryTest,UpdateWatcherTag)275 TEST_F(FileSystemProviderRegistryTest, UpdateWatcherTag) {
276   MountOptions options(kFileSystemId, kDisplayName);
277   options.writable = true;
278   options.supports_notify_tag = true;
279 
280   ProvidedFileSystemInfo file_system_info(
281       kProviderId, options, base::FilePath(FILE_PATH_LITERAL("/a/b/c")),
282       false /* configurable */, true /* watchable */, extensions::SOURCE_FILE,
283       IconSet());
284 
285   Watchers watchers;
286   watchers[WatcherKey(fake_watcher_.entry_path, fake_watcher_.recursive)] =
287       fake_watcher_;
288 
289   registry_->RememberFileSystem(file_system_info, watchers);
290 
291   fake_watcher_.last_tag = "updated-tag";
292   registry_->UpdateWatcherTag(file_system_info, fake_watcher_);
293 
294   sync_preferences::TestingPrefServiceSyncable* const pref_service =
295       profile_->GetTestingPrefService();
296   ASSERT_TRUE(pref_service);
297 
298   const base::DictionaryValue* const extensions =
299       pref_service->GetDictionary(prefs::kFileSystemProviderMounted);
300   ASSERT_TRUE(extensions);
301 
302   const base::DictionaryValue* file_systems = NULL;
303   ASSERT_TRUE(extensions->GetDictionaryWithoutPathExpansion(
304       kProviderId.ToString(), &file_systems));
305   EXPECT_EQ(1u, file_systems->size());
306 
307   const base::Value* file_system_value = NULL;
308   const base::DictionaryValue* file_system = NULL;
309   ASSERT_TRUE(
310       file_systems->GetWithoutPathExpansion(kFileSystemId, &file_system_value));
311   ASSERT_TRUE(file_system_value->GetAsDictionary(&file_system));
312 
313   const base::DictionaryValue* watchers_value = NULL;
314   ASSERT_TRUE(file_system->GetDictionaryWithoutPathExpansion(kPrefKeyWatchers,
315                                                              &watchers_value));
316 
317   const base::DictionaryValue* watcher = NULL;
318   ASSERT_TRUE(watchers_value->GetDictionaryWithoutPathExpansion(
319       fake_watcher_.entry_path.value(), &watcher));
320 
321   std::string last_tag;
322   EXPECT_TRUE(watcher->GetStringWithoutPathExpansion(kPrefKeyWatcherLastTag,
323                                                      &last_tag));
324   EXPECT_EQ(fake_watcher_.last_tag, last_tag);
325 }
326 
327 }  // namespace file_system_provider
328 }  // namespace chromeos
329