blob: 1d128c97a791cfc27343e65eaad108fa1fe45b90 [file] [log] [blame]
//
// 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