| // 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 "components/fuchsia_component_support/annotations_manager.h" |
| |
| #include "base/fuchsia/mem_buffer_util.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace fuchsia_component_support { |
| |
| namespace { |
| |
| class AnnotationsManagerTest : public testing::Test { |
| protected: |
| AnnotationsManagerTest() = default; |
| |
| base::test::SingleThreadTaskEnvironment task_environment_{ |
| base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; |
| }; |
| |
| constexpr char kGlobalNamespace[] = "global"; |
| |
| constexpr char kAnnotationName1[] = "annotation1"; |
| constexpr char kAnnotationName2[] = "annotation2"; |
| |
| constexpr char kAnnotationValue1[] = "annotation_value1"; |
| constexpr char kAnnotationValue2[] = "annotation_value2"; |
| |
| TEST(MakeAnnotationTest, KeyNamespaceAndName) { |
| auto annotation = MakeAnnotation(kAnnotationName1, "ignored"); |
| EXPECT_EQ(annotation.key.namespace_, kGlobalNamespace); |
| EXPECT_EQ(annotation.key.value, kAnnotationName1); |
| } |
| |
| TEST(MakeAnnotationTest, BooleanValue) { |
| auto annotation = MakeBoolAnnotation(kAnnotationName1, true); |
| ASSERT_TRUE(annotation.value.is_text()); |
| EXPECT_EQ(annotation.value.text(), "true"); |
| } |
| |
| TEST(MakeAnnotationTest, IntegerValue) { |
| auto annotation = MakeIntAnnotation(kAnnotationName1, 12345678); |
| ASSERT_TRUE(annotation.value.is_text()); |
| EXPECT_EQ(annotation.value.text(), "12345678"); |
| } |
| |
| TEST(MakeAnnotationTest, TextValue) { |
| constexpr char kSmallText[] = "This is a short text annotation"; |
| auto annotation = MakeAnnotation(kAnnotationName1, kSmallText); |
| ASSERT_TRUE(annotation.value.is_text()); |
| EXPECT_EQ(annotation.value.text(), kSmallText); |
| } |
| |
| TEST(MakeAnnotationTest, LargeTextValue) { |
| const std::string large_text(1024, 'a'); |
| auto annotation = MakeAnnotation(kAnnotationName1, large_text); |
| ASSERT_TRUE(annotation.value.is_buffer()); |
| auto value = base::StringFromMemBuffer(annotation.value.buffer()); |
| EXPECT_EQ(value, large_text); |
| } |
| |
| // Basic verification that a single client can connect. |
| TEST_F(AnnotationsManagerTest, SingleClient) { |
| AnnotationsManager annotations; |
| |
| fuchsia::element::AnnotationControllerPtr ptr; |
| annotations.Connect(ptr.NewRequest()); |
| } |
| |
| // Verifies that multiple clients can connect. |
| TEST_F(AnnotationsManagerTest, MultipleClients) { |
| AnnotationsManager annotations; |
| |
| fuchsia::element::AnnotationControllerPtr ptr1; |
| annotations.Connect(ptr1.NewRequest()); |
| fuchsia::element::AnnotationControllerPtr ptr2; |
| annotations.Connect(ptr2.NewRequest()); |
| } |
| |
| // Verifies that WatchAnnotations() hanging-get behaviour: |
| // - first call returns all annotations. |
| // - watch when nothing has changed "hangs" until the next change. |
| // - watch after a change returns immediately. |
| TEST_F(AnnotationsManagerTest, WatchAnnotationsHangingGet) { |
| AnnotationsManager annotations; |
| |
| fuchsia::element::AnnotationControllerPtr controller; |
| annotations.Connect(controller.NewRequest()); |
| |
| // Dispatch an initial watch, that will return immediately. |
| bool received_annotations = false; |
| controller->WatchAnnotations([&received_annotations](auto annotations) { |
| EXPECT_EQ(annotations.response().annotations.size(), 0u); |
| received_annotations = true; |
| }); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(received_annotations); |
| |
| // Set a new watch, which should not return immediately, since nothing has |
| // changed. |
| received_annotations = false; |
| controller->WatchAnnotations([&received_annotations](auto annotations) { |
| EXPECT_EQ(annotations.response().annotations.size(), 1u); |
| received_annotations = true; |
| }); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(received_annotations); |
| |
| // Set an annotation and spin the loop, to verify that the update is reported. |
| std::vector<fuchsia::element::Annotation> to_set; |
| to_set.push_back(MakeAnnotation(kAnnotationName1, kAnnotationValue1)); |
| annotations.UpdateAnnotations(std::move(to_set)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(received_annotations); |
| |
| // Change the annotation and spin the loop, then call WatchAnnotations() to |
| // verify that it returns immediately. |
| to_set.clear(); |
| to_set.push_back(MakeAnnotation(kAnnotationName1, kAnnotationValue2)); |
| annotations.UpdateAnnotations(std::move(to_set)); |
| received_annotations = false; |
| controller->WatchAnnotations([&received_annotations](auto annotations) { |
| EXPECT_EQ(annotations.response().annotations.size(), 1u); |
| received_annotations = true; |
| }); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(received_annotations); |
| } |
| |
| // Verifies that WatchAnnotations() returns all annotations when any annotation |
| // is updated, not just the changed one. |
| TEST_F(AnnotationsManagerTest, WatchAnnotationsReturnsAllAnnotations) { |
| AnnotationsManager annotations; |
| |
| fuchsia::element::AnnotationControllerPtr controller; |
| annotations.Connect(controller.NewRequest()); |
| |
| // Set multiple annotations. |
| std::vector<fuchsia::element::Annotation> annotations_to_set; |
| annotations_to_set.push_back( |
| MakeAnnotation(kAnnotationName1, kAnnotationValue1)); |
| annotations_to_set.push_back( |
| MakeAnnotation(kAnnotationName2, kAnnotationValue2)); |
| annotations.UpdateAnnotations(std::move(annotations_to_set)); |
| |
| // Dispatch an initial watch, that will return immediately. |
| bool received_annotations = false; |
| controller->WatchAnnotations([&received_annotations](auto annotations) { |
| EXPECT_EQ(annotations.response().annotations.size(), 2u); |
| received_annotations = true; |
| }); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(received_annotations); |
| |
| // Update a single annotation, then call WatchAnnotations() again to verify |
| // that all annotations including unchanged ones are returned. |
| std::vector<fuchsia::element::Annotation> to_set; |
| to_set.push_back(MakeAnnotation(kAnnotationName1, kAnnotationValue2)); |
| annotations.UpdateAnnotations(std::move(to_set)); |
| received_annotations = false; |
| controller->WatchAnnotations([&received_annotations](auto annotations) { |
| EXPECT_EQ(annotations.response().annotations.size(), 2u); |
| received_annotations = true; |
| }); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(received_annotations); |
| } |
| |
| // Verifies insertion of a new annotation via UpdateAnnotations(). |
| TEST_F(AnnotationsManagerTest, UpdateAnnotationsSetsAnnotations) { |
| AnnotationsManager annotations; |
| |
| auto all_annotations = annotations.GetAnnotations(); |
| ASSERT_EQ(all_annotations.size(), 0u); |
| |
| std::vector<fuchsia::element::Annotation> to_set; |
| to_set.push_back(MakeAnnotation(kAnnotationName1, kAnnotationValue1)); |
| annotations.UpdateAnnotations(std::move(to_set)); |
| |
| all_annotations = annotations.GetAnnotations(); |
| ASSERT_EQ(all_annotations.size(), 1u); |
| EXPECT_EQ(all_annotations[0].key.value, kAnnotationName1); |
| ASSERT_TRUE(all_annotations[0].value.is_text()); |
| EXPECT_EQ(all_annotations[0].value.text(), kAnnotationValue1); |
| } |
| |
| // Verifies update of existing annotation via UpdateAnnotations(). |
| TEST_F(AnnotationsManagerTest, UpdateAnnotationsUpdatesAnnotations) { |
| AnnotationsManager annotations; |
| |
| std::vector<fuchsia::element::Annotation> to_set; |
| to_set.push_back(MakeAnnotation(kAnnotationName1, kAnnotationValue1)); |
| annotations.UpdateAnnotations(std::move(to_set)); |
| to_set.push_back(MakeAnnotation(kAnnotationName1, kAnnotationValue2)); |
| annotations.UpdateAnnotations(std::move(to_set)); |
| |
| auto all_annotations = annotations.GetAnnotations(); |
| ASSERT_EQ(all_annotations.size(), 1u); |
| EXPECT_EQ(all_annotations[0].key.value, kAnnotationName1); |
| ASSERT_TRUE(all_annotations[0].value.is_text()); |
| EXPECT_EQ(all_annotations[0].value.text(), kAnnotationValue2); |
| } |
| |
| // Verifies that multiple annotations can be updated or insert in a single call. |
| TEST_F(AnnotationsManagerTest, UpdateAnnotationsMultipleAnnotations) { |
| AnnotationsManager annotations; |
| |
| std::vector<fuchsia::element::Annotation> annotations_to_set; |
| annotations_to_set.push_back( |
| MakeAnnotation(kAnnotationName1, kAnnotationValue1)); |
| annotations_to_set.push_back( |
| MakeAnnotation(kAnnotationName2, kAnnotationValue2)); |
| annotations.UpdateAnnotations(std::move(annotations_to_set)); |
| |
| auto all_annotations = annotations.GetAnnotations(); |
| ASSERT_EQ(all_annotations.size(), 2u); |
| EXPECT_EQ(all_annotations[0].key.value, kAnnotationName1); |
| ASSERT_TRUE(all_annotations[0].value.is_text()); |
| EXPECT_EQ(all_annotations[0].value.text(), kAnnotationValue1); |
| EXPECT_EQ(all_annotations[1].key.value, kAnnotationName2); |
| ASSERT_TRUE(all_annotations[1].value.is_text()); |
| EXPECT_EQ(all_annotations[1].value.text(), kAnnotationValue2); |
| } |
| |
| // Verifies that the controller fails the operation if duplicate annotations |
| // appear in the same bulk-UpdateAnnotations operation. |
| TEST_F(AnnotationsManagerTest, |
| UpdateAnnotationsMultipleAnnotations_WithDuplicate) { |
| AnnotationsManager annotations; |
| |
| std::vector<fuchsia::element::Annotation> annotations_to_set; |
| annotations_to_set.push_back( |
| MakeAnnotation(kAnnotationName1, kAnnotationValue1)); |
| annotations_to_set.push_back( |
| MakeAnnotation(kAnnotationName1, kAnnotationValue2)); |
| |
| EXPECT_FALSE(annotations.UpdateAnnotations(std::move(annotations_to_set))); |
| } |
| |
| } // namespace |
| |
| } // namespace fuchsia_component_support |