| // Copyright 2017 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 "peridot/bin/context_engine/index.h" |
| |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <lib/fidl/cpp/clone.h> |
| |
| #include "gtest/gtest.h" |
| |
| namespace modular { |
| namespace { |
| |
| TEST(IndexTest, Encode_Basic) { |
| // Basic encoding correctness: |
| // * null case(s) |
| // * values are indexed along with their key |
| auto type = fuchsia::modular::ContextValueType::ENTITY; |
| EXPECT_EQ(1lu, internal::EncodeMetadataAndType(type, nullptr).size()); |
| EXPECT_EQ(1lu, internal::EncodeMetadataAndType( |
| type, fuchsia::modular::ContextMetadata::New()) |
| .size()); |
| |
| auto meta = fuchsia::modular::ContextMetadata::New(); |
| meta->story = fuchsia::modular::StoryMetadata::New(); |
| EXPECT_EQ(1lu, internal::EncodeMetadataAndType(type, meta).size()); |
| |
| meta->story->id = "value"; |
| auto out = internal::EncodeMetadataAndType(type, meta); |
| EXPECT_EQ(2lu, out.size()); |
| |
| meta->mod = fuchsia::modular::ModuleMetadata::New(); |
| meta->mod->url = "value"; |
| out = internal::EncodeMetadataAndType(type, meta); |
| // Even though we use "value" as the value for both fields above, we should |
| // see them encoded differently since they are for different fields. |
| EXPECT_EQ(3lu, out.size()); |
| } |
| |
| TEST(IndexTest, Encode_Differences) { |
| auto kEntity = fuchsia::modular::ContextValueType::ENTITY; |
| auto kStory = fuchsia::modular::ContextValueType::STORY; |
| // Encoding two entirely different fuchsia::modular::ContextMetadata structs |
| // should produce two non-intersecting sets of encodings. |
| fuchsia::modular::ContextMetadata meta1; |
| meta1.story = fuchsia::modular::StoryMetadata::New(); |
| meta1.story->id = "story1"; |
| meta1.story->focused = fuchsia::modular::FocusedState::New(); |
| meta1.story->focused->state = fuchsia::modular::FocusedStateState::FOCUSED; |
| meta1.mod = fuchsia::modular::ModuleMetadata::New(); |
| meta1.mod->url = "url1"; |
| meta1.mod->path = fidl::VectorPtr<std::string>::New(0); |
| meta1.mod->path.push_back("1"); |
| meta1.mod->path.push_back("2"); |
| meta1.mod->focused = fuchsia::modular::FocusedState::New(); |
| meta1.mod->focused->state = fuchsia::modular::FocusedStateState::FOCUSED; |
| meta1.entity = fuchsia::modular::EntityMetadata::New(); |
| meta1.entity->topic = "topic1"; |
| meta1.entity->type = fidl::VectorPtr<std::string>::New(0); |
| meta1.entity->type.push_back("type1"); |
| meta1.entity->type.push_back("type2"); |
| |
| auto meta2 = fuchsia::modular::ContextMetadata::New(); |
| meta2->story = fuchsia::modular::StoryMetadata::New(); |
| meta2->story->id = "story2"; |
| meta2->story->focused = fuchsia::modular::FocusedState::New(); |
| meta2->story->focused->state = |
| fuchsia::modular::FocusedStateState::NOT_FOCUSED; |
| meta2->mod = fuchsia::modular::ModuleMetadata::New(); |
| meta2->mod->url = "url2"; |
| meta2->mod->path = fidl::VectorPtr<std::string>::New(0); |
| meta2->mod->path.push_back("2"); |
| meta2->mod->focused = fuchsia::modular::FocusedState::New(); |
| meta2->mod->focused->state = fuchsia::modular::FocusedStateState::NOT_FOCUSED; |
| meta2->entity = fuchsia::modular::EntityMetadata::New(); |
| meta2->entity->topic = "topic2"; |
| meta2->entity->type = fidl::VectorPtr<std::string>::New(0); |
| meta2->entity->type.push_back("type3"); |
| meta2->entity->type.push_back("type4"); |
| meta2->entity->type.push_back("type5"); |
| |
| auto encoded1 = internal::EncodeMetadataAndType(kEntity, meta1); |
| auto encoded2 = internal::EncodeMetadataAndType(kStory, meta2); |
| |
| // Every field we set has a value here. entity->type fields each get their |
| // own. |
| EXPECT_EQ(9lu, encoded1.size()); |
| EXPECT_EQ(10lu, encoded2.size()); |
| |
| std::set<std::string> intersection; |
| std::set_intersection(encoded1.begin(), encoded1.end(), encoded2.begin(), |
| encoded2.end(), |
| std::inserter(intersection, intersection.begin())); |
| EXPECT_TRUE(intersection.empty()); |
| |
| // If we start changing some values to be equal, we should see encoded values |
| // included. |
| meta2->story->focused->state = fuchsia::modular::FocusedStateState::FOCUSED; |
| meta2->entity->type->at(1) = "type2"; |
| |
| encoded1 = internal::EncodeMetadataAndType(kEntity, meta1); |
| encoded2 = internal::EncodeMetadataAndType(kEntity, meta2); |
| intersection.clear(); |
| std::set_intersection(encoded1.begin(), encoded1.end(), encoded2.begin(), |
| encoded2.end(), |
| std::inserter(intersection, intersection.begin())); |
| EXPECT_EQ(3lu, intersection.size()); |
| } |
| |
| TEST(IndexTest, AddRemoveQuery) { |
| auto kEntity = fuchsia::modular::ContextValueType::ENTITY; |
| auto kStory = fuchsia::modular::ContextValueType::STORY; |
| // We do not need to test that querying works for every single field in |
| // fuchsia::modular::ContextMetadata: between the Encode tests above, and the |
| // knowledge that Encode is used internally by ContextIndex, we can test here |
| // for correct query results for a subset of fields, and infer that the same |
| // behavior would happen for other fields. |
| ContextIndex index; |
| fuchsia::modular::ContextMetadata meta1; |
| meta1.story = fuchsia::modular::StoryMetadata::New(); |
| meta1.story->id = "story1"; |
| meta1.entity = fuchsia::modular::EntityMetadata::New(); |
| meta1.entity->topic = "topic1"; |
| meta1.entity->type = fidl::VectorPtr<std::string>::New(0); |
| meta1.entity->type.push_back("type1"); |
| meta1.entity->type.push_back("type2"); |
| |
| index.Add("e1", kEntity, meta1); |
| |
| // This query won't match because story->id != "s". |
| auto query1 = fuchsia::modular::ContextMetadata::New(); |
| query1->story = fuchsia::modular::StoryMetadata::New(); |
| query1->story->id = "s"; // Won't match. |
| std::set<std::string> res; |
| index.Query(kEntity, query1, &res); |
| EXPECT_TRUE(res.empty()); |
| |
| // This one still won't because kStory != kEntity. |
| query1->story->id = "story1"; |
| res.clear(); |
| index.Query(kStory, query1, &res); |
| EXPECT_TRUE(res.empty()); |
| |
| // This one will. |
| query1->story->id = "story1"; |
| res.clear(); |
| index.Query(kEntity, query1, &res); |
| EXPECT_EQ(1ul, res.size()); |
| EXPECT_TRUE(res.find("e1") != res.end()); |
| |
| // Add more to the query that we know will match. |
| query1->entity = fuchsia::modular::EntityMetadata::New(); |
| query1->entity->type.push_back("type1"); |
| res.clear(); |
| index.Query(kEntity, query1, &res); |
| EXPECT_EQ(1ul, res.size()); |
| EXPECT_TRUE(res.find("e1") != res.end()); |
| |
| // Add a new entity. |
| auto meta2 = fidl::Clone(meta1); |
| meta2.entity->type.push_back("type3"); |
| index.Add("e2", kEntity, meta2); |
| |
| res.clear(); |
| index.Query(kEntity, query1, &res); |
| EXPECT_EQ(2ul, res.size()); |
| EXPECT_TRUE(res.find("e1") != res.end()); |
| EXPECT_TRUE(res.find("e2") != res.end()); |
| |
| // Changing the query's type param to "type3" should only return "e2". |
| query1->entity->type->at(0) = "type3"; |
| res.clear(); |
| index.Query(kEntity, query1, &res); |
| EXPECT_EQ(1ul, res.size()); |
| EXPECT_TRUE(res.find("e2") != res.end()); |
| |
| // And removing "e2" from the index makes it no longer appear in |
| // query results. |
| index.Remove("e2", kEntity, meta2); |
| res.clear(); |
| index.Query(kEntity, query1, &res); |
| EXPECT_TRUE(res.empty()); |
| |
| // But "e1" is still there. |
| query1->entity->type->at(0) = "type2"; |
| res.clear(); |
| index.Query(kEntity, query1, &res); |
| EXPECT_EQ(1ul, res.size()); |
| EXPECT_TRUE(res.find("e1") != res.end()); |
| } |
| |
| } // namespace |
| } // namespace modular |