// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/file_system_provider/registry.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ash/file_system_provider/icon_set.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_info.h"
#include "chrome/common/extensions/api/file_system_provider_capabilities/file_system_provider_capabilities_handler.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/user_prefs/user_prefs.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash::file_system_provider {
namespace {
const char kTemporaryOrigin[] =
"chrome-extension://abcabcabcabcabcabcabcabcabcabcabcabca/";
const char kPersistentOrigin[] =
"chrome-extension://efgefgefgefgefgefgefgefgefgefgefgefge/";
const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
const char kDisplayName[] = "Camera Pictures";
const ProviderId kProviderId = ProviderId::CreateFromExtensionId(kExtensionId);
// The dot in the file system ID is there in order to check that saving to
// preferences works correctly. File System ID is used as a key in
// a base::Value, so it has to be stored without path expansion.
const char kFileSystemId[] = "camera/pictures/id .!@#$%^&*()_+";
const int kOpenedFilesLimit = 5;
// Stores a provided file system information in preferences together with a
// fake watcher.
void 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) {
// Warning. Updating this code means that backward compatibility may be
// broken, what is unexpected and should be avoided.
sync_preferences::TestingPrefServiceSyncable* const pref_service =
profile->GetTestingPrefService();
ASSERT_TRUE(pref_service);
base::Value::Dict extensions;
base::Value::Dict file_system;
file_system.Set(kPrefKeyFileSystemId, kFileSystemId);
file_system.Set(kPrefKeyDisplayName, kDisplayName);
file_system.Set(kPrefKeyWritable, writable);
file_system.Set(kPrefKeySupportsNotifyTag, supports_notify_tag);
file_system.Set(kPrefKeyOpenedFilesLimit, opened_files_limit);
// Remember watchers.
base::Value::Dict watcher_value;
watcher_value.Set(kPrefKeyWatcherEntryPath, watcher.entry_path.value());
watcher_value.Set(kPrefKeyWatcherRecursive, watcher.recursive);
watcher_value.Set(kPrefKeyWatcherLastTag, watcher.last_tag);
base::Value::List persistent_origins_value;
for (const auto& subscriber_it : watcher.subscribers) {
if (subscriber_it.second.persistent)
persistent_origins_value.Append(subscriber_it.first.spec());
}
watcher_value.Set(kPrefKeyWatcherPersistentOrigins,
std::move(persistent_origins_value));
base::Value::Dict watchers;
watchers.Set(watcher.entry_path.value(), std::move(watcher_value));
file_system.Set(kPrefKeyWatchers, std::move(watchers));
base::Value::Dict file_systems;
file_systems.Set(kFileSystemId, std::move(file_system));
extensions.Set(kProviderId.ToString(), std::move(file_systems));
pref_service->SetDict(prefs::kFileSystemProviderMounted,
std::move(extensions));
}
} // namespace
class FileSystemProviderRegistryTest : public testing::Test {
protected:
FileSystemProviderRegistryTest() : profile_(nullptr) {}
~FileSystemProviderRegistryTest() override = default;
void SetUp() override {
profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
ASSERT_TRUE(profile_manager_->SetUp());
profile_ = profile_manager_->CreateTestingProfile("[email protected]");
registry_ = std::make_unique<Registry>(profile_);
fake_watcher_.entry_path = base::FilePath(FILE_PATH_LITERAL("/a/b/c"));
fake_watcher_.recursive = true;
fake_watcher_.subscribers[GURL(kTemporaryOrigin)].origin =
GURL(kTemporaryOrigin);
fake_watcher_.subscribers[GURL(kTemporaryOrigin)].persistent = false;
fake_watcher_.subscribers[GURL(kPersistentOrigin)].origin =
GURL(kPersistentOrigin);
fake_watcher_.subscribers[GURL(kPersistentOrigin)].persistent = true;
fake_watcher_.last_tag = "hello-world";
}
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<TestingProfileManager> profile_manager_;
raw_ptr<TestingProfile> profile_;
std::unique_ptr<RegistryInterface> registry_;
Watcher fake_watcher_;
};
TEST_F(FileSystemProviderRegistryTest, RestoreFileSystems) {
// Create a fake entry in the preferences.
RememberFakeFileSystem(profile_, kProviderId, kFileSystemId, kDisplayName,
/*writable=*/true, /*supports_notify_tag=*/true,
kOpenedFilesLimit, fake_watcher_);
std::unique_ptr<RegistryInterface::RestoredFileSystems>
restored_file_systems = registry_->RestoreFileSystems(kProviderId);
ASSERT_EQ(1u, restored_file_systems->size());
const RegistryInterface::RestoredFileSystem& restored_file_system =
restored_file_systems->at(0);
EXPECT_EQ(kProviderId, restored_file_system.provider_id);
EXPECT_EQ(kFileSystemId, restored_file_system.options.file_system_id);
EXPECT_EQ(kDisplayName, restored_file_system.options.display_name);
EXPECT_TRUE(restored_file_system.options.writable);
EXPECT_TRUE(restored_file_system.options.supports_notify_tag);
EXPECT_EQ(kOpenedFilesLimit, restored_file_system.options.opened_files_limit);
ASSERT_EQ(1u, restored_file_system.watchers.size());
const auto& restored_watcher_it = restored_file_system.watchers.find(
WatcherKey(fake_watcher_.entry_path, fake_watcher_.recursive));
ASSERT_NE(restored_file_system.watchers.end(), restored_watcher_it);
EXPECT_EQ(fake_watcher_.entry_path, restored_watcher_it->second.entry_path);
EXPECT_EQ(fake_watcher_.recursive, restored_watcher_it->second.recursive);
EXPECT_EQ(fake_watcher_.last_tag, restored_watcher_it->second.last_tag);
}
TEST_F(FileSystemProviderRegistryTest, RememberFileSystem) {
MountOptions options(kFileSystemId, kDisplayName);
options.writable = true;
options.supports_notify_tag = true;
options.opened_files_limit = kOpenedFilesLimit;
ProvidedFileSystemInfo file_system_info(
kProviderId, options, base::FilePath(FILE_PATH_LITERAL("/a/b/c")),
/*configurable=*/false, /*watchable=*/true, extensions::SOURCE_FILE,
IconSet());
Watchers watchers;
watchers[WatcherKey(fake_watcher_.entry_path, fake_watcher_.recursive)] =
fake_watcher_;
registry_->RememberFileSystem(file_system_info, watchers);
sync_preferences::TestingPrefServiceSyncable* const pref_service =
profile_->GetTestingPrefService();
ASSERT_TRUE(pref_service);
const base::Value::Dict& extensions =
pref_service->GetDict(prefs::kFileSystemProviderMounted);
const base::Value::Dict* file_systems =
extensions.FindDict(kProviderId.ToString());
ASSERT_TRUE(file_systems);
EXPECT_EQ(1u, file_systems->size());
const base::Value::Dict* file_system = file_systems->FindDict(kFileSystemId);
ASSERT_TRUE(file_system);
const std::string* file_system_id =
file_system->FindString(kPrefKeyFileSystemId);
EXPECT_TRUE(file_system_id);
EXPECT_EQ(kFileSystemId, *file_system_id);
const std::string* display_name =
file_system->FindString(kPrefKeyDisplayName);
EXPECT_TRUE(display_name);
EXPECT_EQ(kDisplayName, *display_name);
std::optional<bool> writable = file_system->FindBool(kPrefKeyWritable);
EXPECT_TRUE(writable.has_value());
EXPECT_TRUE(writable.value());
std::optional<bool> supports_notify_tag =
file_system->FindBool(kPrefKeySupportsNotifyTag);
EXPECT_TRUE(supports_notify_tag.has_value());
EXPECT_TRUE(supports_notify_tag.value());
std::optional<int> opened_files_limit =
file_system->FindInt(kPrefKeyOpenedFilesLimit);
EXPECT_TRUE(opened_files_limit.has_value());
EXPECT_EQ(kOpenedFilesLimit, opened_files_limit.value());
const base::Value::Dict* watchers_dict =
file_system->FindDict(kPrefKeyWatchers);
ASSERT_TRUE(watchers_dict);
const base::Value::Dict* watcher =
watchers_dict->FindDict(fake_watcher_.entry_path.value());
ASSERT_TRUE(watcher);
const std::string* entry_path = watcher->FindString(kPrefKeyWatcherEntryPath);
EXPECT_TRUE(entry_path);
EXPECT_EQ(fake_watcher_.entry_path.value(), *entry_path);
std::optional<bool> recursive = watcher->FindBool(kPrefKeyWatcherRecursive);
EXPECT_TRUE(recursive.has_value());
EXPECT_EQ(fake_watcher_.recursive, recursive.value());
const std::string* last_tag = watcher->FindString(kPrefKeyWatcherLastTag);
EXPECT_TRUE(last_tag);
EXPECT_EQ(fake_watcher_.last_tag, *last_tag);
const base::Value::List* persistent_origins =
watcher->FindList(kPrefKeyWatcherPersistentOrigins);
ASSERT_TRUE(persistent_origins);
ASSERT_GT(fake_watcher_.subscribers.size(), persistent_origins->size());
ASSERT_EQ(1u, persistent_origins->size());
const std::string* persistent_origin =
persistent_origins->front().GetIfString();
ASSERT_TRUE(persistent_origin);
const auto& fake_subscriber_it =
fake_watcher_.subscribers.find(GURL(*persistent_origin));
ASSERT_NE(fake_watcher_.subscribers.end(), fake_subscriber_it);
EXPECT_TRUE(fake_subscriber_it->second.persistent);
}
TEST_F(FileSystemProviderRegistryTest, ForgetFileSystem) {
// Create a fake file systems in the preferences.
RememberFakeFileSystem(profile_, kProviderId, kFileSystemId, kDisplayName,
/*writable=*/true, /*supports_notify_tag=*/true,
kOpenedFilesLimit, fake_watcher_);
registry_->ForgetFileSystem(kProviderId, kFileSystemId);
sync_preferences::TestingPrefServiceSyncable* const pref_service =
profile_->GetTestingPrefService();
ASSERT_TRUE(pref_service);
const base::Value::Dict& extensions =
pref_service->GetDict(prefs::kFileSystemProviderMounted);
const base::Value::Dict* file_systems =
extensions.FindDict(kProviderId.GetExtensionId());
EXPECT_FALSE(file_systems);
}
TEST_F(FileSystemProviderRegistryTest, UpdateWatcherTag) {
MountOptions options(kFileSystemId, kDisplayName);
options.writable = true;
options.supports_notify_tag = true;
ProvidedFileSystemInfo file_system_info(
kProviderId, options, base::FilePath(FILE_PATH_LITERAL("/a/b/c")),
/*configurable=*/false, /*watchable=*/true, extensions::SOURCE_FILE,
IconSet());
Watchers watchers;
watchers[WatcherKey(fake_watcher_.entry_path, fake_watcher_.recursive)] =
fake_watcher_;
registry_->RememberFileSystem(file_system_info, watchers);
fake_watcher_.last_tag = "updated-tag";
registry_->UpdateWatcherTag(file_system_info, fake_watcher_);
sync_preferences::TestingPrefServiceSyncable* const pref_service =
profile_->GetTestingPrefService();
ASSERT_TRUE(pref_service);
const base::Value::Dict& extensions =
pref_service->GetDict(prefs::kFileSystemProviderMounted);
const base::Value::Dict* file_systems =
extensions.FindDict(kProviderId.ToString());
ASSERT_TRUE(file_systems);
EXPECT_EQ(1u, file_systems->size());
const base::Value::Dict* file_system = file_systems->FindDict(kFileSystemId);
ASSERT_TRUE(file_system);
const base::Value::Dict* watchers_dict =
file_system->FindDict(kPrefKeyWatchers);
ASSERT_TRUE(watchers_dict);
const base::Value::Dict* watcher =
watchers_dict->FindDict(fake_watcher_.entry_path.value());
ASSERT_TRUE(watcher);
const std::string* last_tag = watcher->FindString(kPrefKeyWatcherLastTag);
EXPECT_TRUE(last_tag);
EXPECT_EQ(fake_watcher_.last_tag, *last_tag);
}
} // namespace ash::file_system_provider