blob: 92606d4c07044342478bc25b42a7b9ab47cfdbbe [file] [log] [blame]
// Copyright 2022 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/fuchsia/element_manager_impl.h"
#include <lib/sys/cpp/component_context.h>
#include <lib/sys/cpp/service_directory.h>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/fuchsia/process_context.h"
#include "base/fuchsia/test_component_context_for_process.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace {
struct AnnotationKeyProxy {
AnnotationKeyProxy(
std::string key,
std::string namespace_ = "namespace") // NOLINT(runtime/explicit)
: key(std::move(key)), namespace_(std::move(namespace_)) {}
std::string key;
std::string namespace_;
fuchsia::element::AnnotationKey ToAnnotationKey() const {
fuchsia::element::AnnotationKey result;
result.value = key;
result.namespace_ = namespace_;
return result;
}
};
struct AnnotationProxy {
AnnotationProxy(std::string key, std::string value)
: key(std::move(key)), value(std::move(value)) {}
AnnotationProxy(std::string key, std::string namespace_, std::string value)
: key(std::move(key), std::move(namespace_)), value(std::move(value)) {}
AnnotationKeyProxy key;
std::string value;
fuchsia::element::Annotation ToAnnotation() const {
fuchsia::element::Annotation result;
result.key = key.ToAnnotationKey();
result.value =
fuchsia::element::AnnotationValue::WithText(std::string(value));
return result;
}
};
std::vector<fuchsia::element::AnnotationKey> TestAnnotationKeys(
std::initializer_list<AnnotationKeyProxy> keys) {
std::vector<fuchsia::element::AnnotationKey> results;
for (const auto& key : keys) {
results.push_back(key.ToAnnotationKey());
}
return results;
}
std::vector<fuchsia::element::Annotation> TestAnnotations(
std::initializer_list<AnnotationProxy> annotations) {
std::vector<fuchsia::element::Annotation> results;
for (const auto& annotation : annotations) {
results.push_back(annotation.ToAnnotation());
}
return results;
}
class ElementManagerImplTest : public testing::Test {
public:
ElementManagerImplTest()
: element_manager_(base::ComponentContextForProcess()->outgoing().get(),
base::BindLambdaForTesting(
[&](const base::CommandLine& command_line) {
received_command_line_ = command_line;
return true;
})) {
element_manager_.set_have_browser_callback_for_test(
base::BindLambdaForTesting([&]() { return browser_count_ > 0; }));
}
protected:
fuchsia::element::ManagerPtr GetElementManagerPtr() {
return test_context_.published_services()
->Connect<fuchsia::element::Manager>();
}
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
base::TestComponentContextForProcess test_context_;
absl::optional<base::CommandLine> received_command_line_;
ElementManagerImpl element_manager_;
int browser_count_ = 0;
};
TEST_F(ElementManagerImplTest, CorrectSpec) {
auto element_manager = GetElementManagerPtr();
for (const char* url : {
"fuchsia-pkg://fuchsia.com/chrome#meta/chrome.cm",
"fuchsia-pkg://chromium.org/chrome#meta/chrome.cm",
"fuchsia-pkg://chrome.com/chrome#meta/chrome.cm",
"http://www.example.com",
"https://www.example.com",
}) {
fuchsia::element::Spec spec;
spec.set_component_url(url);
base::RunLoop run_loop;
absl::optional<fuchsia::element::Manager_ProposeElement_Result>
received_result;
element_manager->ProposeElement(
std::move(spec), {},
[&](fuchsia::element::Manager_ProposeElement_Result result) {
received_result = std::move(result);
run_loop.Quit();
});
run_loop.Run();
ASSERT_TRUE(received_result);
EXPECT_FALSE(received_result->is_err()) << url;
EXPECT_TRUE(received_command_line_);
}
}
TEST_F(ElementManagerImplTest, IncorrectSpec) {
auto element_manager = GetElementManagerPtr();
for (const char* url : {
"foobar",
"fuchsia-pkg://chromium.org/web_engine#meta/chrome.cm",
"fuchsia-pkg://chromium.org/chrome#meta/web_engine.cm",
"fuchsia-pkg://chromium.org/chrome",
"fuchsia-pkg://chromium.org/#meta/chrome.cm",
"fuchsia-pkg://chromium.org/mychrome#meta/chrome.cm",
"chrome#meta/chrome.cm",
}) {
fuchsia::element::Spec spec;
spec.set_component_url(url);
base::RunLoop run_loop;
absl::optional<fuchsia::element::Manager_ProposeElement_Result>
received_result;
element_manager->ProposeElement(
std::move(spec), {},
[&](fuchsia::element::Manager_ProposeElement_Result result) {
received_result = std::move(result);
run_loop.Quit();
});
run_loop.Run();
ASSERT_TRUE(received_result);
EXPECT_TRUE(received_result->is_err()) << url;
EXPECT_FALSE(received_command_line_);
}
}
TEST_F(ElementManagerImplTest, ElementControllerClosedOnInvalidSpec) {
auto element_manager = GetElementManagerPtr();
fuchsia::element::ControllerPtr controller;
fuchsia::element::Spec valid_spec;
valid_spec.set_component_url(
"fuchsia-pkg://chromium.org/chrome#meta/chrome.cm");
element_manager->ProposeElement(
std::move(valid_spec), controller.NewRequest(),
[&](auto result) { ASSERT_TRUE(result.is_response()); });
task_environment_.RunUntilIdle();
EXPECT_TRUE(controller.is_bound());
fuchsia::element::Spec invalid_spec;
invalid_spec.set_component_url("foobar");
controller = fuchsia::element::ControllerPtr();
element_manager->ProposeElement(
std::move(invalid_spec), controller.NewRequest(),
[&](auto result) { ASSERT_FALSE(result.is_response()); });
task_environment_.RunUntilIdle();
EXPECT_FALSE(controller.is_bound());
}
TEST_F(ElementManagerImplTest, Annotations) {
EXPECT_EQ(0u, element_manager_.annotations_manager().GetAnnotations().size());
auto element_manager = GetElementManagerPtr();
fuchsia::element::ControllerPtr controller;
{
base::RunLoop run_loop;
fuchsia::element::Spec valid_spec;
valid_spec.set_component_url(
"fuchsia-pkg://chromium.org/chrome#meta/chrome.cm");
*valid_spec.mutable_annotations() = TestAnnotations(
{{"key1", "value1"}, {"key2", "value2"}, {"key3", "value3"}});
element_manager->ProposeElement(std::move(valid_spec),
controller.NewRequest(), [&](auto result) {
ASSERT_TRUE(result.is_response());
run_loop.Quit();
});
run_loop.Run();
}
std::vector<fuchsia::element::Annotation> annotations =
element_manager_.annotations_manager().GetAnnotations();
EXPECT_EQ(3u, annotations.size());
for (const auto* key : {"key1", "key2", "key3"}) {
EXPECT_TRUE(base::Contains(annotations, key, [](const auto& annotation) {
return annotation.key.value;
}));
}
{
base::RunLoop run_loop;
controller->GetAnnotations([&](auto result) {
ASSERT_TRUE(result.is_response());
annotations = std::move(result.response().annotations);
run_loop.Quit();
});
run_loop.Run();
}
EXPECT_EQ(3u, annotations.size());
{
base::RunLoop run_loop;
controller->UpdateAnnotations(
TestAnnotations({{"key1", "other_value1"},
{"key4", "value4"},
{"key3", "other_namespace", "value5"}}),
TestAnnotationKeys(
{{"key1", "nonexistant_namespace"}, {"key2"}, {"key3"}}),
[&](auto result) {
ASSERT_TRUE(result.is_response());
run_loop.Quit();
});
run_loop.Run();
}
annotations = element_manager_.annotations_manager().GetAnnotations();
EXPECT_EQ(3u, annotations.size());
for (const auto* key : {"key1", "key3", "key4"}) {
EXPECT_TRUE(base::Contains(annotations, key, [](const auto& annotation) {
return annotation.key.value;
}));
}
}
TEST_F(ElementManagerImplTest, ElementControllerAndBrowserLifeCycle) {
auto element_manager = GetElementManagerPtr();
fuchsia::element::ControllerPtr controller;
fuchsia::element::Spec valid_spec;
valid_spec.set_component_url(
"fuchsia-pkg://chromium.org/chrome#meta/chrome.cm");
element_manager->ProposeElement(
std::move(valid_spec), controller.NewRequest(),
[&](auto result) { ASSERT_TRUE(result.is_response()); });
task_environment_.RunUntilIdle();
EXPECT_TRUE(controller.is_bound());
// Notifying the manager while there is still browser alive should not
// disconnect the controller.
browser_count_ = 1;
static_cast<BrowserListObserver*>(&element_manager_)
->OnBrowserRemoved(nullptr);
task_environment_.RunUntilIdle();
EXPECT_TRUE(controller.is_bound());
// When the manager is notified for the last window, the controller should be
// unbound.
browser_count_ = 0;
static_cast<BrowserListObserver*>(&element_manager_)
->OnBrowserRemoved(nullptr);
task_environment_.RunUntilIdle();
EXPECT_FALSE(controller.is_bound());
// A new connection to the manager should allow a new controller to be bounded
valid_spec.set_component_url(
"fuchsia-pkg://chromium.org/chrome#meta/chrome.cm");
element_manager->ProposeElement(
std::move(valid_spec), controller.NewRequest(),
[&](auto result) { ASSERT_TRUE(result.is_response()); });
task_environment_.RunUntilIdle();
EXPECT_TRUE(controller.is_bound());
}
} // namespace