blob: 362d2ae42d77b6c1865b904d8cfc46603510ecaf [file] [log] [blame]
// Copyright 2018 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/extensions/file_manager/event_router.h"
#include <memory>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/values.h"
#include "chrome/browser/ash/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/ash/file_manager/fake_disk_mount_manager.h"
#include "chrome/browser/ash/file_manager/io_task.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/volume_manager_factory.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/test_event_router.h"
#include "extensions/common/extension.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_backend.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/test/test_file_system_context.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace file_manager {
TEST(EventRouterTest, PopulateCrostiniEvent) {
extensions::api::file_manager_private::CrostiniEvent ext_event;
url::Origin ext_origin = url::Origin::Create(
extensions::Extension::GetBaseURLFromExtensionId("extensionid"));
EventRouter::PopulateCrostiniEvent(
ext_event,
extensions::api::file_manager_private::CROSTINI_EVENT_TYPE_UNSHARE,
"vmname", ext_origin, "mountname", "filesystemname", "/full/path");
EXPECT_EQ(ext_event.event_type,
extensions::api::file_manager_private::CROSTINI_EVENT_TYPE_UNSHARE);
EXPECT_EQ(ext_event.vm_name, "vmname");
EXPECT_EQ(ext_event.entries.size(), 1u);
base::Value::Dict ext_props;
ext_props.Set(
"fileSystemRoot",
"filesystem:chrome-extension://extensionid/external/mountname/");
ext_props.Set("fileSystemName", "filesystemname");
ext_props.Set("fileFullPath", "/full/path");
ext_props.Set("fileIsDirectory", true);
EXPECT_EQ(ext_event.entries[0].additional_properties, ext_props);
extensions::api::file_manager_private::CrostiniEvent swa_event;
url::Origin swa_origin = url::Origin::Create(
GURL("chrome://file-manager/this-part-should-not-be-in?the=event"));
EventRouter::PopulateCrostiniEvent(
swa_event,
extensions::api::file_manager_private::CROSTINI_EVENT_TYPE_SHARE,
"vmname", swa_origin, "mountname", "filesystemname", "/full/path");
EXPECT_EQ(swa_event.event_type,
extensions::api::file_manager_private::CROSTINI_EVENT_TYPE_SHARE);
EXPECT_EQ(swa_event.vm_name, "vmname");
EXPECT_EQ(swa_event.entries.size(), 1u);
base::Value::Dict swa_props;
swa_props.Set("fileSystemRoot",
"filesystem:chrome://file-manager/external/mountname/");
swa_props.Set("fileSystemName", "filesystemname");
swa_props.Set("fileFullPath", "/full/path");
swa_props.Set("fileIsDirectory", true);
EXPECT_EQ(swa_event.entries[0].additional_properties, swa_props);
}
namespace {
using ::base::test::RunClosure;
using ::testing::_;
using ::testing::AllOf;
using ::testing::Field;
// Observes the `BroadcastEvent` operation that is emitted by the event router.
// The mock methods are used to assert expectations on the return results.
class TestEventRouterObserver
: public extensions::TestEventRouter::EventObserver {
public:
explicit TestEventRouterObserver(extensions::TestEventRouter* event_router)
: event_router_(event_router) {
event_router_->AddEventObserver(this);
}
~TestEventRouterObserver() override {
event_router_->RemoveEventObserver(this);
}
TestEventRouterObserver(const TestEventRouterObserver&) = delete;
TestEventRouterObserver& operator=(const TestEventRouterObserver&) = delete;
// TestEventRouter::EventObserver:
MOCK_METHOD(void, OnBroadcastEvent, (const extensions::Event&), (override));
MOCK_METHOD(void,
OnDispatchEventToExtension,
(const std::string&, const extensions::Event&),
(override));
private:
raw_ptr<extensions::TestEventRouter> event_router_;
};
class FileManagerEventRouterTest : public testing::Test {
public:
FileManagerEventRouterTest() = default;
FileManagerEventRouterTest(const FileManagerEventRouterTest&) = delete;
FileManagerEventRouterTest& operator=(const FileManagerEventRouterTest&) =
delete;
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
profile_ =
std::make_unique<TestingProfile>(base::FilePath(temp_dir_.GetPath()));
file_system_context_ = storage::CreateFileSystemContextForTesting(
nullptr, temp_dir_.GetPath());
VolumeManagerFactory::GetInstance()->SetTestingFactory(
profile_.get(),
base::BindLambdaForTesting([this](content::BrowserContext* context) {
return std::unique_ptr<KeyedService>(std::make_unique<VolumeManager>(
Profile::FromBrowserContext(context), nullptr, nullptr,
&disk_mount_manager_, nullptr,
VolumeManager::GetMtpStorageInfoCallback()));
}));
storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
file_manager::util::GetDownloadsMountPointName(profile_.get()),
storage::kFileSystemTypeLocal, storage::FileSystemMountOption(),
temp_dir_.GetPath());
file_manager::util::GetFileSystemContextForSourceURL(
profile_.get(), GURL("chrome-extension://abc"))
->external_backend()
->GrantFileAccessToOrigin(
url::Origin::Create(GURL("chrome-extension://abc")),
base::FilePath(file_manager::util::GetDownloadsMountPointName(
profile_.get())));
}
const io_task::EntryStatus CreateSuccessfulEntryStatusForFileName(
const std::string& file_name) {
const base::FilePath file_path = temp_dir_.GetPath().Append(file_name);
EXPECT_TRUE(base::WriteFile(file_path, base::RandBytesAsString(32)));
storage::FileSystemURL url =
file_system_context_->CreateCrackedFileSystemURL(
kTestStorageKey, storage::kFileSystemTypeTest, file_path);
return io_task::EntryStatus(std::move(url), base::File::FILE_OK);
}
content::BrowserTaskEnvironment task_environment_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<TestingProfile> profile_;
const blink::StorageKey kTestStorageKey =
blink::StorageKey::CreateFromStringForTesting("chrome-extension://abc");
scoped_refptr<storage::FileSystemContext> file_system_context_;
file_manager::FakeDiskMountManager disk_mount_manager_;
};
// A matcher that matches an `extensions::Event::event_args` and attempts to
// extract the "outputs" key. It then looks at the output at `index` and matches
// the `field` against the `expected_value`.
MATCHER_P3(ExpectEventArgString, index, field, expected_value, "") {
EXPECT_GE(arg.size(), 1u);
const base::Value::List* outputs = arg[0].GetDict().FindList("outputs");
EXPECT_TRUE(outputs) << "The outputs field is not available on the event";
EXPECT_GT(outputs->size(), index)
<< "The supplied index on outputs is not available, size: "
<< outputs->size() << ", index: " << index;
const std::string* actual_value =
(*outputs)[index].GetDict().FindString(field);
EXPECT_TRUE(actual_value) << "Could not find the string with key: " << field;
return testing::ExplainMatchResult(expected_value, *actual_value,
result_listener);
}
TEST_F(FileManagerEventRouterTest, OnIOTaskStatusForTrash) {
// Setup event routers.
extensions::TestEventRouter* test_event_router =
extensions::CreateAndUseTestEventRouter(profile_.get());
TestEventRouterObserver observer(test_event_router);
auto event_router = std::make_unique<EventRouter>(profile_.get());
event_router->ForceBroadcastingForTesting(true);
io_task::EntryStatus source_entry =
CreateSuccessfulEntryStatusForFileName("foo.txt");
io_task::EntryStatus output_entry =
CreateSuccessfulEntryStatusForFileName("bar.txt");
std::vector<io_task::EntryStatus> source_entries;
source_entries.push_back(std::move(source_entry));
std::vector<io_task::EntryStatus> output_entries;
output_entries.push_back(std::move(output_entry));
// Setup the ProgressStatus event that expects
file_manager::io_task::ProgressStatus status;
status.type = file_manager::io_task::OperationType::kTrash;
status.state = file_manager::io_task::State::kSuccess;
status.sources = std::move(source_entries);
status.outputs = std::move(output_entries);
base::RunLoop run_loop;
EXPECT_CALL(
observer,
OnBroadcastEvent(Field(
&extensions::Event::event_args,
AllOf(ExpectEventArgString(0u, "fileFullPath", "/bar.txt"),
ExpectEventArgString(0u, "fileSystemName", "Downloads"),
ExpectEventArgString(
0u, "fileSystemRoot",
"filesystem:chrome-extension://abc/external/Downloads/")))))
.WillOnce(RunClosure(run_loop.QuitClosure()));
event_router->OnIOTaskStatus(status);
run_loop.Run();
}
} // namespace
} // namespace file_manager