blob: 4cc8c5b8c942d884e49f64a7fb2efb225b521c6d [file] [log] [blame]
// Copyright 2021 The Fuchsia 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 "src/modular/bin/sessionmgr/annotations.h"
#include <fuchsia/element/cpp/fidl.h>
#include <fuchsia/modular/testing/cpp/fidl.h>
#include <fuchsia/testing/modular/cpp/fidl.h>
#include <lib/fit/function.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/modular/bin/sessionmgr/testing/annotations_matchers.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_element.h"
#include "src/modular/lib/modular_test_harness/cpp/fake_graphical_presenter.h"
#include "src/modular/lib/modular_test_harness/cpp/test_harness_fixture.h"
namespace {
constexpr auto kTestAnnotationKey = "test_annotation_key";
constexpr auto kTestAnnotationValue = "test_annotation_value";
using element::annotations::AnnotationEq;
using ::testing::ByRef;
using ::testing::ElementsAre;
class AnnotationsTest : public modular_testing::TestHarnessFixture {
protected:
AnnotationsTest()
: fake_graphical_presenter_(
modular_testing::FakeGraphicalPresenter::CreateWithDefaultOptions()),
element_(modular_testing::FakeElement::CreateWithDefaultOptions()) {}
fuchsia::element::ManagerPtr& element_manager() { return element_manager_; }
fuchsia::element::AnnotationControllerPtr& annotation_controller() {
return annotation_controller_;
}
modular_testing::FakeGraphicalPresenter* graphical_presenter() {
return fake_graphical_presenter_.get();
}
modular_testing::FakeElement* element() { return element_.get(); }
void StartSession() {
modular_testing::TestHarnessBuilder builder;
builder.InterceptSessionShell(fake_graphical_presenter_->BuildInterceptOptions());
builder.InterceptComponent(element_->BuildInterceptOptions());
bool graphical_presenter_connected = false;
fake_graphical_presenter_->set_on_graphical_presenter_connected([&]() {
graphical_presenter_connected = true;
fake_graphical_presenter_->set_on_graphical_presenter_error([&](zx_status_t status) {});
});
fake_graphical_presenter_->set_on_graphical_presenter_error([&](zx_status_t status) {
FX_PLOGS(FATAL, status) << "Failed to connect to FakeGraphicalPresenter";
FX_NOTREACHED();
});
builder.BuildAndRun(test_harness());
fuchsia::modular::testing::ModularService request;
request.set_element_manager(element_manager_.NewRequest());
test_harness()->ConnectToModularService(std::move(request));
EXPECT_FALSE(fake_graphical_presenter_->is_running());
RunLoopUntil([&] { return fake_graphical_presenter_->is_running(); });
RunLoopUntil([&] { return graphical_presenter_connected; });
// Add Event Listeners
graphical_presenter()->set_on_present_view(
[&](fuchsia::element::ViewSpec view_spec,
fidl::InterfaceHandle<fuchsia::element::AnnotationController> annotation_controller) {
annotation_controller_ = annotation_controller.Bind();
});
}
void CheckElementControllerAnnotations(
fuchsia::element::ControllerPtr& element_controller,
fit::function<void(std::vector<fuchsia::element::Annotation>&)> check_annotations) {
bool checked_annotation{false};
element_controller->GetAnnotations(
[&](fuchsia::element::AnnotationController_GetAnnotations_Result result) {
ASSERT_FALSE(result.is_err());
check_annotations(result.response().annotations);
checked_annotation = true;
});
RunLoopUntil([&] { return checked_annotation; });
}
void CheckAnnotationControllerAnnotations(
fit::function<void(std::vector<fuchsia::element::Annotation>&)> check_annotations) {
bool checked_annotation{false};
annotation_controller()->GetAnnotations(
[&](fuchsia::element::AnnotationController_GetAnnotations_Result result) {
ASSERT_FALSE(result.is_err());
check_annotations(result.response().annotations);
checked_annotation = true;
});
RunLoopUntil([&] { return checked_annotation; });
}
private:
fuchsia::element::ManagerPtr element_manager_;
fuchsia::element::AnnotationControllerPtr annotation_controller_;
std::unique_ptr<modular_testing::FakeGraphicalPresenter> fake_graphical_presenter_;
std::unique_ptr<modular_testing::FakeElement> element_;
};
// Tests that updates to an element's annotations using the element's controller are reflected
// in an AnnotationController associated with the same story.
TEST_F(AnnotationsTest, UpdateAnnotationsThroughElementController) {
StartSession();
// Propose an element without annotations.
bool is_proposed{false};
fuchsia::element::ControllerPtr element_controller;
element_manager()->ProposeElement(fidl::Clone(element()->spec()), element_controller.NewRequest(),
[&](fuchsia::element::Manager_ProposeElement_Result result) {
EXPECT_FALSE(result.is_err());
is_proposed = true;
});
RunLoopUntil([&]() { return is_proposed && element()->is_running(); });
// Check that the element has no annotations.
fuchsia::element::Annotation annotation_to_check;
CheckElementControllerAnnotations(
element_controller, [](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_TRUE(annotations_to_check.empty());
});
CheckAnnotationControllerAnnotations(
[](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_TRUE(annotations_to_check.empty());
});
// Update the element's annotations through the element's controller.
auto element_annotation = fuchsia::element::Annotation{
.key = modular::annotations::ToElementAnnotationKey(kTestAnnotationKey),
.value = fuchsia::element::AnnotationValue::WithText(kTestAnnotationValue)};
std::vector<fuchsia::element::Annotation> annotations_to_set;
annotations_to_set.push_back(fidl::Clone(element_annotation));
bool did_update_annotations{false};
element_controller->UpdateAnnotations(
std::move(annotations_to_set),
/*annotations_to_delete=*/{},
[&](fuchsia::element::AnnotationController_UpdateAnnotations_Result result) {
EXPECT_FALSE(result.is_err());
did_update_annotations = true;
});
RunLoopUntil([&] { return did_update_annotations; });
// Assert the annotation controller reflects updated annotations.
CheckAnnotationControllerAnnotations(
[&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_THAT(annotations_to_check, ElementsAre(AnnotationEq(ByRef(element_annotation))));
});
}
// Tests that updates to an element's annotations using an AnnotationController are reflected
// in the element's controller.
TEST_F(AnnotationsTest, UpdateAnnotationsThroughAnnotationController) {
StartSession();
// Propose an element without annotations.
bool is_proposed{false};
fuchsia::element::ControllerPtr element_controller;
element_manager()->ProposeElement(fidl::Clone(element()->spec()), element_controller.NewRequest(),
[&](fuchsia::element::Manager_ProposeElement_Result result) {
EXPECT_FALSE(result.is_err());
is_proposed = true;
});
RunLoopUntil([&]() { return is_proposed && element()->is_running(); });
// Check that the element has no annotations.
fuchsia::element::Annotation annotation_to_check;
CheckElementControllerAnnotations(
element_controller, [](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_TRUE(annotations_to_check.empty());
});
CheckAnnotationControllerAnnotations(
[](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_TRUE(annotations_to_check.empty());
});
// Update the element's annotations through the AnnotationController.
auto element_annotation = fuchsia::element::Annotation{
.key = modular::annotations::ToElementAnnotationKey(kTestAnnotationKey),
.value = fuchsia::element::AnnotationValue::WithText(kTestAnnotationValue)};
std::vector<fuchsia::element::Annotation> annotations_to_set;
annotations_to_set.push_back(fidl::Clone(element_annotation));
bool did_update_annotations{false};
annotation_controller()->UpdateAnnotations(
std::move(annotations_to_set),
/*annotations_to_delete=*/{},
[&](fuchsia::element::AnnotationController_UpdateAnnotations_Result result) {
EXPECT_FALSE(result.is_err());
did_update_annotations = true;
});
RunLoopUntil([&] { return did_update_annotations; });
// Assert the element controller reflects updated annotations.
CheckElementControllerAnnotations(
element_controller, [&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_THAT(annotations_to_check, ElementsAre(AnnotationEq(ByRef(element_annotation))));
});
}
// Tests that deleting an element's annotation using the element's controller is reflected
// in an AnnotationController associated with the same element.
TEST_F(AnnotationsTest, DeleteAnnotationsThroughElementController) {
StartSession();
// Create an ElementSpec with an annotation.
auto element_annotation = fuchsia::element::Annotation{
.key = modular::annotations::ToElementAnnotationKey(kTestAnnotationKey),
.value = fuchsia::element::AnnotationValue::WithText(kTestAnnotationValue)};
auto element_spec = fidl::Clone(element()->spec());
element_spec.mutable_annotations()->push_back(fidl::Clone(element_annotation));
// Propose the element
bool is_proposed{false};
fuchsia::element::ControllerPtr element_controller;
element_manager()->ProposeElement(std::move(element_spec), element_controller.NewRequest(),
[&](fuchsia::element::Manager_ProposeElement_Result result) {
EXPECT_FALSE(result.is_err());
is_proposed = true;
});
RunLoopUntil([&]() { return is_proposed && element()->is_running(); });
// Check that the element has annotations.
CheckElementControllerAnnotations(
element_controller, [&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_THAT(annotations_to_check, ElementsAre(AnnotationEq(ByRef(element_annotation))));
});
CheckAnnotationControllerAnnotations(
[&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_THAT(annotations_to_check, ElementsAre(AnnotationEq(ByRef(element_annotation))));
});
// Delete the element's annotation through the element's controller.
auto key_to_delete = modular::annotations::ToElementAnnotationKey(kTestAnnotationKey);
std::vector<fuchsia::element::AnnotationKey> annotations_to_delete;
annotations_to_delete.push_back(std::move(key_to_delete));
bool did_update_annotations{false};
element_controller->UpdateAnnotations(
/*annotations_to_set=*/{}, std::move(annotations_to_delete),
[&](fuchsia::element::AnnotationController_UpdateAnnotations_Result result) {
EXPECT_FALSE(result.is_err());
did_update_annotations = true;
});
RunLoopUntil([&] { return did_update_annotations; });
// Assert the annotation controller reflects updated annotations.
CheckAnnotationControllerAnnotations(
[](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_TRUE(annotations_to_check.empty());
});
}
// Tests that deleting an element's annotation using an AnnotationController is reflected
// in an AnnotationController associated with the same element.
TEST_F(AnnotationsTest, DeleteAnnotationsThroughAnnotationController) {
StartSession();
// Create an ElementSpec with an annotation.
auto element_annotation = fuchsia::element::Annotation{
.key = modular::annotations::ToElementAnnotationKey(kTestAnnotationKey),
.value = fuchsia::element::AnnotationValue::WithText(kTestAnnotationValue)};
auto element_spec = fidl::Clone(element()->spec());
element_spec.mutable_annotations()->push_back(fidl::Clone(element_annotation));
// Propose the element
bool is_proposed{false};
fuchsia::element::ControllerPtr element_controller;
element_manager()->ProposeElement(std::move(element_spec), element_controller.NewRequest(),
[&](fuchsia::element::Manager_ProposeElement_Result result) {
EXPECT_FALSE(result.is_err());
is_proposed = true;
});
RunLoopUntil([&]() { return is_proposed && element()->is_running(); });
// Check that the element has annotations.
CheckElementControllerAnnotations(
element_controller, [&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_THAT(annotations_to_check, ElementsAre(AnnotationEq(ByRef(element_annotation))));
});
CheckAnnotationControllerAnnotations(
[&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_THAT(annotations_to_check, ElementsAre(AnnotationEq(ByRef(element_annotation))));
});
// Delete the element's annotation through the AnnotationController.
auto key_to_delete = modular::annotations::ToElementAnnotationKey(kTestAnnotationKey);
std::vector<fuchsia::element::AnnotationKey> annotations_to_delete;
annotations_to_delete.push_back(std::move(key_to_delete));
bool did_update_annotations{false};
annotation_controller()->UpdateAnnotations(
/*annotations_to_set=*/{}, std::move(annotations_to_delete),
[&](fuchsia::element::AnnotationController_UpdateAnnotations_Result result) {
EXPECT_FALSE(result.is_err());
did_update_annotations = true;
});
RunLoopUntil([&] { return did_update_annotations; });
// Assert the element controller reflects updated annotations.
CheckElementControllerAnnotations(
element_controller, [&](std::vector<fuchsia::element::Annotation>& annotations_to_check) {
EXPECT_TRUE(annotations_to_check.empty());
});
}
} // namespace