| // |
| // Copyright (C) 2022 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <unordered_set> |
| |
| #include <gtest/gtest.h> |
| |
| #include "common/libs/utils/files.h" |
| #include "common/libs/utils/json.h" |
| #include "host/commands/cvd/selector/instance_database.h" |
| #include "host/commands/cvd/selector/selector_constants.h" |
| #include "host/commands/cvd/unittests/selector/instance_database_helper.h" |
| |
| /* |
| * SetUp creates a mock ANDROID_HOST_OUT directory where there is |
| * bin/launch_cvd, and a "Workspace" directory where supposedly HOME |
| * directories for each LocalInstanceGroup will be populated. |
| * |
| * InstanceDatabase APIs conduct validity checks: e.g. if the host tool |
| * directory actually has host tools such as launch_cvd, if the "HOME" |
| * directory for the LocalInstanceGroup is actually an existing directory, |
| * and so on. |
| * |
| * With TEST_F(Suite, Test), the following is the class declaration: |
| * class Suite : public testing::Test; |
| * class Suite_Test : public Suite; |
| * |
| * Thus, the set up is done inside the constructur of the Suite base class. |
| * Also, cleaning up the directories and files are done inside the destructor. |
| * If creating files/directories fails, the "Test" is skipped. |
| * |
| */ |
| |
| namespace cuttlefish { |
| namespace selector { |
| |
| TEST_F(CvdInstanceDatabaseTest, Empty) { |
| if (!SetUpOk()) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| ASSERT_TRUE(db.IsEmpty()); |
| ASSERT_TRUE(db.InstanceGroups().empty()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, AddWithInvalidGroupInfo) { |
| if (!SetUpOk()) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| // populate home directories under Workspace() |
| const std::string home{Workspace() + "/" + "meow"}; |
| if (!EnsureDirectoryExists(home).ok()) { |
| // if ever failed, skip |
| GTEST_SKIP() << "Failed to find/create " << home; |
| } |
| const std::string invalid_host_artifacts_path{Workspace() + "/" + "host_out"}; |
| if (!EnsureDirectoryExists(invalid_host_artifacts_path).ok() || |
| !EnsureDirectoryExists(invalid_host_artifacts_path + "/bin").ok()) { |
| GTEST_SKIP() << "Failed to find/create " |
| << invalid_host_artifacts_path + "/bin"; |
| } |
| |
| // group_name : "meow" |
| auto result_bad_home = |
| db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = "/path/to/never/exists", |
| .host_artifacts_path = HostArtifactsPath(), |
| .product_out_path = HostArtifactsPath()}); |
| auto result_bad_host_bin_dir = |
| db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = home, |
| .host_artifacts_path = "/path/to/never/exists", |
| .product_out_path = "/path/to/never/exists"}); |
| auto result_both_bad = |
| db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = "/path/to/never/exists", |
| .host_artifacts_path = "/path/to/never/exists", |
| .product_out_path = "/path/to/never/exists"}); |
| auto result_bad_group_name = |
| db.AddInstanceGroup({.group_name = "0invalid_group_name", |
| .home_dir = home, |
| .host_artifacts_path = HostArtifactsPath(), |
| .product_out_path = HostArtifactsPath()}); |
| // Everything is correct but one thing: the host artifacts directory does not |
| // have host tool files such as launch_cvd |
| auto result_non_qualifying_host_tool_dir = |
| db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = home, |
| .host_artifacts_path = invalid_host_artifacts_path, |
| .product_out_path = invalid_host_artifacts_path}); |
| |
| ASSERT_FALSE(result_bad_home.ok()); |
| ASSERT_FALSE(result_bad_host_bin_dir.ok()); |
| ASSERT_FALSE(result_both_bad.ok()); |
| ASSERT_FALSE(result_bad_group_name.ok()); |
| ASSERT_FALSE(result_non_qualifying_host_tool_dir.ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, AddWithValidGroupInfo) { |
| if (!SetUpOk()) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| const std::string home0{Workspace() + "/" + "home0"}; |
| if (!EnsureDirectoryExists(home0).ok()) { |
| GTEST_SKIP() << "Failed to find/create " << home0; |
| } |
| const std::string home1{Workspace() + "/" + "home1"}; |
| if (!EnsureDirectoryExists(home1).ok()) { |
| GTEST_SKIP() << "Failed to find/create " << home1; |
| } |
| |
| ASSERT_TRUE(db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = home0, |
| .host_artifacts_path = HostArtifactsPath(), |
| .product_out_path = HostArtifactsPath()}) |
| .ok()); |
| ASSERT_TRUE(db.AddInstanceGroup({.group_name = "miaou", |
| .home_dir = home1, |
| .host_artifacts_path = HostArtifactsPath(), |
| .product_out_path = HostArtifactsPath()}) |
| .ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, AddToTakenHome) { |
| if (!SetUpOk()) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| const std::string home{Workspace() + "/" + "my_home"}; |
| if (!EnsureDirectoryExists(home).ok()) { |
| GTEST_SKIP() << "Failed to find/create " << home; |
| } |
| |
| ASSERT_TRUE(db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = home, |
| .host_artifacts_path = HostArtifactsPath(), |
| .product_out_path = HostArtifactsPath()}) |
| .ok()); |
| ASSERT_FALSE(db.AddInstanceGroup({.group_name = "meow", |
| .home_dir = home, |
| .host_artifacts_path = HostArtifactsPath(), |
| .product_out_path = HostArtifactsPath()}) |
| .ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, Clear) { |
| /* AddGroups(name): |
| * HOME: Workspace() + "/" + name |
| * HostArtifactsPath: Workspace() + "/" + "android_host_out" |
| * group_ := LocalInstanceGroup(name, HOME, HostArtifactsPath) |
| */ |
| if (!SetUpOk() || !AddGroups({"nyah", "yah_ong"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| |
| // test Clear() |
| ASSERT_FALSE(db.IsEmpty()); |
| db.Clear(); |
| ASSERT_TRUE(db.IsEmpty()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, SearchGroups) { |
| if (!SetUpOk() || !AddGroups({"myau", "miau"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| const std::string valid_home_search_key{Workspace() + "/" + "myau"}; |
| const std::string invalid_home_search_key{"/no/such/path"}; |
| |
| auto valid_groups = db.FindGroups({kHomeField, valid_home_search_key}); |
| auto valid_group = db.FindGroup({kHomeField, valid_home_search_key}); |
| auto invalid_groups = db.FindGroups({kHomeField, invalid_home_search_key}); |
| auto invalid_group = db.FindGroup({kHomeField, invalid_home_search_key}); |
| |
| ASSERT_TRUE(valid_groups.ok()); |
| ASSERT_EQ(valid_groups->size(), 1); |
| ASSERT_TRUE(valid_group.ok()); |
| |
| ASSERT_TRUE(invalid_groups.ok()); |
| ASSERT_EQ(invalid_groups->size(), 0); |
| ASSERT_FALSE(invalid_group.ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, RemoveGroup) { |
| if (!SetUpOk()) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| if (!AddGroups({"miaaaw", "meow", "mjau"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto eng_group = db.FindGroup({kHomeField, Workspace() + "/" + "meow"}); |
| if (!eng_group.ok()) { |
| GTEST_SKIP() << "meow" |
| << " group was not found."; |
| } |
| |
| ASSERT_TRUE(db.RemoveInstanceGroup(*eng_group)); |
| ASSERT_FALSE(db.RemoveInstanceGroup(*eng_group)); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, AddInstances) { |
| if (!SetUpOk() || !AddGroups({"yah_ong"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| auto kitty_group = db.FindGroup({kHomeField, Workspace() + "/" + "yah_ong"}); |
| if (!kitty_group.ok()) { |
| GTEST_SKIP() << "yah_ong" |
| << " group was not found"; |
| } |
| const auto& instances = kitty_group->Get().Instances(); |
| |
| ASSERT_TRUE(db.AddInstance("yah_ong", 1, "yumi").ok()); |
| ASSERT_FALSE(db.AddInstance("yah_ong", 3, "yumi").ok()); |
| ASSERT_FALSE(db.AddInstance("yah_ong", 1, "tiger").ok()); |
| ASSERT_TRUE(db.AddInstance("yah_ong", 3, "tiger").ok()); |
| for (auto const& instance_unique_ptr : instances) { |
| ASSERT_TRUE(instance_unique_ptr->PerInstanceName() == "yumi" || |
| instance_unique_ptr->PerInstanceName() == "tiger"); |
| } |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, AddInstancesInvalid) { |
| if (!SetUpOk() || !AddGroups({"yah_ong"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| auto kitty_group = db.FindGroup({kHomeField, Workspace() + "/" + "yah_ong"}); |
| if (!kitty_group.ok()) { |
| GTEST_SKIP() << "yah_ong" |
| << " group was not found"; |
| } |
| |
| ASSERT_FALSE(db.AddInstance("yah_ong", 1, "!yumi").ok()); |
| ASSERT_FALSE(db.AddInstance("yah_ong", 7, "ti ger").ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, FindByInstanceId) { |
| // The start of set up |
| if (!SetUpOk()) { |
| GTEST_SKIP() << Error().msg; |
| } |
| if (!AddGroups({"miau", "nyah"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| // per_instance_name could be the same if the parent groups are different. |
| std::vector<InstanceInfo> miau_group_instance_id_name_pairs{ |
| {1, "8"}, {10, "tv-instance"}}; |
| std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{ |
| {7, "my_favorite_phone"}, {11, "tv-instance"}, {3, "3_"}}; |
| auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"}); |
| auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"}); |
| if (!miau_group.ok() || !nyah_group.ok()) { |
| GTEST_SKIP() << "miau or nyah group" |
| << " group was not found"; |
| } |
| if (!AddInstances("miau", miau_group_instance_id_name_pairs) || |
| !AddInstances("nyah", nyah_group_instance_id_name_pairs)) { |
| GTEST_SKIP() << Error().msg; |
| } |
| // The end of set up |
| |
| auto result1 = db.FindInstance({kInstanceIdField, std::to_string(1)}); |
| auto result10 = db.FindInstance({kInstanceIdField, std::to_string(10)}); |
| auto result7 = db.FindInstance({kInstanceIdField, std::to_string(7)}); |
| auto result11 = db.FindInstance({kInstanceIdField, std::to_string(11)}); |
| auto result3 = db.FindInstance({kInstanceIdField, std::to_string(3)}); |
| auto result_invalid = db.FindInstance({kInstanceIdField, std::to_string(20)}); |
| |
| ASSERT_TRUE(result1.ok()); |
| ASSERT_TRUE(result10.ok()); |
| ASSERT_TRUE(result7.ok()); |
| ASSERT_TRUE(result11.ok()); |
| ASSERT_TRUE(result3.ok()); |
| ASSERT_EQ(result1->Get().PerInstanceName(), "8"); |
| ASSERT_EQ(result10->Get().PerInstanceName(), "tv-instance"); |
| ASSERT_EQ(result7->Get().PerInstanceName(), "my_favorite_phone"); |
| ASSERT_EQ(result11->Get().PerInstanceName(), "tv-instance"); |
| ASSERT_EQ(result3->Get().PerInstanceName(), "3_"); |
| ASSERT_FALSE(result_invalid.ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, FindByPerInstanceName) { |
| // starting set up |
| if (!SetUpOk() || !AddGroups({"miau", "nyah"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| std::vector<InstanceInfo> miau_group_instance_id_name_pairs{ |
| {1, "8"}, {10, "tv_instance"}}; |
| std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{ |
| {7, "my_favorite_phone"}, {11, "tv_instance"}}; |
| auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"}); |
| auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"}); |
| if (!miau_group.ok() || !nyah_group.ok()) { |
| GTEST_SKIP() << "miau or nyah " |
| << " group was not found"; |
| } |
| if (!AddInstances("miau", miau_group_instance_id_name_pairs) || |
| !AddInstances("nyah", nyah_group_instance_id_name_pairs)) { |
| GTEST_SKIP() << Error().msg; |
| } |
| // end of set up |
| |
| auto result1 = db.FindInstance({kInstanceNameField, "8"}); |
| auto result10_and_11 = db.FindInstances({kInstanceNameField, "tv_instance"}); |
| auto result7 = db.FindInstance({kInstanceNameField, "my_favorite_phone"}); |
| auto result_invalid = |
| db.FindInstance({kInstanceNameField, "name_never_seen"}); |
| |
| ASSERT_TRUE(result1.ok()); |
| ASSERT_TRUE(result10_and_11.ok()); |
| ASSERT_TRUE(result7.ok()); |
| ASSERT_EQ(result10_and_11->size(), 2); |
| ASSERT_EQ(result1->Get().InstanceId(), 1); |
| ASSERT_EQ(result7->Get().InstanceId(), 7); |
| ASSERT_FALSE(result_invalid.ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, FindInstancesByGroupName) { |
| // starting set up |
| if (!SetUpOk() || !AddGroups({"miau", "nyah"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{ |
| {7, "my_favorite_phone"}, {11, "tv_instance"}}; |
| auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"}); |
| if (!nyah_group.ok()) { |
| GTEST_SKIP() << "nyah group was not found"; |
| } |
| if (!AddInstances("nyah", nyah_group_instance_id_name_pairs)) { |
| GTEST_SKIP() << Error().msg; |
| } |
| // end of set up |
| |
| auto result_nyah = db.FindInstances({kGroupNameField, "nyah"}); |
| auto result_invalid = db.FindInstance({kGroupNameField, "name_never_seen"}); |
| |
| ASSERT_TRUE(result_nyah.ok()); |
| std::set<std::string> nyah_instance_names; |
| for (const auto& instance : *result_nyah) { |
| nyah_instance_names.insert(instance.Get().PerInstanceName()); |
| } |
| std::set<std::string> expected{"my_favorite_phone", "tv_instance"}; |
| ASSERT_EQ(nyah_instance_names, expected); |
| ASSERT_FALSE(result_invalid.ok()); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, FindGroupByPerInstanceName) { |
| // starting set up |
| if (!SetUpOk() || !AddGroups({"miau", "nyah"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| std::vector<InstanceInfo> miau_group_instance_id_name_pairs{ |
| {1, "8"}, {10, "tv_instance"}}; |
| std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{ |
| {7, "my_favorite_phone"}, {11, "tv_instance"}}; |
| auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"}); |
| auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"}); |
| if (!miau_group.ok() || !nyah_group.ok()) { |
| GTEST_SKIP() << "miau or nyah " |
| << " group was not found"; |
| } |
| if (!AddInstances("miau", miau_group_instance_id_name_pairs) || |
| !AddInstances("nyah", nyah_group_instance_id_name_pairs)) { |
| GTEST_SKIP() << Error().msg; |
| } |
| // end of set up |
| |
| auto result_miau = db.FindGroups({kInstanceNameField, "8"}); |
| auto result_both = db.FindGroups({kInstanceNameField, "tv_instance"}); |
| auto result_nyah = db.FindGroups({kInstanceNameField, "my_favorite_phone"}); |
| auto result_invalid = db.FindGroups({kInstanceNameField, "name_never_seen"}); |
| |
| ASSERT_TRUE(result_miau.ok()); |
| ASSERT_TRUE(result_both.ok()); |
| ASSERT_TRUE(result_nyah.ok()); |
| ASSERT_TRUE(result_invalid.ok()); |
| ASSERT_EQ(result_miau->size(), 1); |
| ASSERT_EQ(result_both->size(), 2); |
| ASSERT_EQ(result_nyah->size(), 1); |
| ASSERT_TRUE(result_invalid->empty()) |
| << "result_invalid should be empty but with size: " |
| << result_invalid->size(); |
| } |
| |
| TEST_F(CvdInstanceDatabaseTest, AddInstancesTogether) { |
| // starting set up |
| if (!SetUpOk() || !AddGroups({"miau"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| std::vector<InstanceDatabase::InstanceInfo> miau_group_instance_id_name_pairs{ |
| {1, "8"}, {10, "tv_instance"}}; |
| auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"}); |
| if (!miau_group.ok()) { |
| GTEST_SKIP() << "miau group was not found"; |
| } |
| |
| auto add_result = db.AddInstances("miau", miau_group_instance_id_name_pairs); |
| ASSERT_TRUE(add_result.ok()) << add_result.error().Trace(); |
| |
| auto result_8 = db.FindInstance({kInstanceNameField, "8"}); |
| auto result_tv = db.FindInstance({kInstanceNameField, "tv_instance"}); |
| |
| ASSERT_TRUE(result_8.ok()) << result_8.error().Trace(); |
| ASSERT_TRUE(result_tv.ok()) << result_tv.error().Trace(); |
| } |
| |
| TEST_F(CvdInstanceDatabaseJsonTest, DumpLoadDumpCompare) { |
| // starting set up |
| if (!SetUpOk() || !AddGroups({"miau"})) { |
| GTEST_SKIP() << Error().msg; |
| } |
| auto& db = GetDb(); |
| std::vector<InstanceDatabase::InstanceInfo> miau_group_instance_id_name_pairs{ |
| {1, "8"}, {10, "tv_instance"}}; |
| auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"}); |
| if (!miau_group.ok()) { |
| GTEST_SKIP() << "miau group was not found"; |
| } |
| auto add_result = db.AddInstances("miau", miau_group_instance_id_name_pairs); |
| if (!add_result.ok()) { |
| GTEST_SKIP() << "Adding instances are not being tested in this test case."; |
| } |
| |
| /* |
| * Dumping to json, clearing up the DB, loading from the json, |
| * |
| */ |
| auto serialized_db = db.Serialize(); |
| if (!db.RemoveInstanceGroup("miau")) { |
| // not testing RemoveInstanceGroup |
| GTEST_SKIP() << "miau had to be added."; |
| } |
| auto json_parsing = ParseJson(serialized_db.toStyledString()); |
| ASSERT_TRUE(json_parsing.ok()) << serialized_db << std::endl |
| << " is not a valid json."; |
| auto load_result = db.LoadFromJson(serialized_db); |
| ASSERT_TRUE(load_result.ok()) << load_result.error().Trace(); |
| { |
| // re-look up the group and the instances |
| auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"}); |
| ASSERT_TRUE(miau_group.ok()) << miau_group.error().Trace(); |
| auto result_8 = db.FindInstance({kInstanceNameField, "8"}); |
| auto result_tv = db.FindInstance({kInstanceNameField, "tv_instance"}); |
| |
| ASSERT_TRUE(result_8.ok()) << result_8.error().Trace(); |
| ASSERT_TRUE(result_tv.ok()) << result_tv.error().Trace(); |
| } |
| } |
| |
| } // namespace selector |
| } // namespace cuttlefish |