blob: 8ee4ead4661c5919ceff6fd55c80410cdd6883c2 [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// 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/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 TestElementManagerImpl : public testing::Test {
public:
TestElementManagerImpl()
: 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(TestElementManagerImpl, TestCorrectSpec) {
fuchsia::element::Spec spec;
spec.set_component_url("fuchsia-pkg://fuchsia.com/chrome#meta/chrome.cm");
auto element_manager = GetElementManagerPtr();
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());
EXPECT_TRUE(received_command_line_);
}
TEST_F(TestElementManagerImpl, TestIncorrectSpec) {
fuchsia::element::Spec spec;
spec.set_component_url("foobar");
auto element_manager = GetElementManagerPtr();
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());
EXPECT_FALSE(received_command_line_);
}
TEST_F(TestElementManagerImpl, TestController) {
auto element_manager = GetElementManagerPtr();
fuchsia::element::ControllerPtr controller;
fuchsia::element::Spec valid_spec;
valid_spec.set_component_url(
"fuchsia-pkg://fuchsia.com/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(TestElementManagerImpl, Annotations) {
EXPECT_EQ(0u, element_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://fuchsia.com/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_.GetAnnotations();
EXPECT_EQ(3u, annotations.size());
for (const auto* key : {"key1", "key2", "key3"}) {
EXPECT_NE(annotations.end(),
std::find_if(annotations.begin(), annotations.end(),
[&](const auto& annotation) {
return annotation.key.value == key;
}));
}
{
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_.GetAnnotations();
EXPECT_EQ(3u, annotations.size());
for (const auto* key : {"key1", "key3", "key4"}) {
EXPECT_NE(annotations.end(),
std::find_if(annotations.begin(), annotations.end(),
[&](const auto& annotation) {
return annotation.key.value == key;
}));
}
}
TEST_F(TestElementManagerImpl, ControllerLifeCycle) {
auto element_manager = GetElementManagerPtr();
fuchsia::element::ControllerPtr controller;
fuchsia::element::Spec valid_spec;
valid_spec.set_component_url(
"fuchsia-pkg://fuchsia.com/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://fuchsia.com/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