• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2022 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include <algorithm>
17 #include <iostream>
18 #include <unordered_set>
19 
20 #include <gtest/gtest.h>
21 
22 #include "common/libs/utils/files.h"
23 #include "common/libs/utils/json.h"
24 #include "host/commands/cvd/selector/instance_database.h"
25 #include "host/commands/cvd/selector/selector_constants.h"
26 #include "host/commands/cvd/unittests/selector/instance_database_helper.h"
27 
28 /*
29  * SetUp creates a mock ANDROID_HOST_OUT directory where there is
30  * bin/launch_cvd, and a "Workspace" directory where supposedly HOME
31  * directories for each LocalInstanceGroup will be populated.
32  *
33  * InstanceDatabase APIs conduct validity checks: e.g. if the host tool
34  * directory actually has host tools such as launch_cvd, if the "HOME"
35  * directory for the LocalInstanceGroup is actually an existing directory,
36  * and so on.
37  *
38  * With TEST_F(Suite, Test), the following is the class declaration:
39  *  class Suite : public testing::Test;
40  *  class Suite_Test : public Suite;
41  *
42  * Thus, the set up is done inside the constructur of the Suite base class.
43  * Also, cleaning up the directories and files are done inside the destructor.
44  * If creating files/directories fails, the "Test" is skipped.
45  *
46  */
47 
48 namespace cuttlefish {
49 namespace selector {
50 
TEST_F(CvdInstanceDatabaseTest,Empty)51 TEST_F(CvdInstanceDatabaseTest, Empty) {
52   if (!SetUpOk()) {
53     GTEST_SKIP() << Error().msg;
54   }
55   auto& db = GetDb();
56   ASSERT_TRUE(db.IsEmpty());
57   ASSERT_TRUE(db.InstanceGroups().empty());
58 }
59 
TEST_F(CvdInstanceDatabaseTest,AddWithInvalidGroupInfo)60 TEST_F(CvdInstanceDatabaseTest, AddWithInvalidGroupInfo) {
61   if (!SetUpOk()) {
62     GTEST_SKIP() << Error().msg;
63   }
64   auto& db = GetDb();
65   // populate home directories under Workspace()
66   const std::string home{Workspace() + "/" + "meow"};
67   if (!EnsureDirectoryExists(home).ok()) {
68     // if ever failed, skip
69     GTEST_SKIP() << "Failed to find/create " << home;
70   }
71   const std::string invalid_host_artifacts_path{Workspace() + "/" + "host_out"};
72   if (!EnsureDirectoryExists(invalid_host_artifacts_path).ok() ||
73       !EnsureDirectoryExists(invalid_host_artifacts_path + "/bin").ok()) {
74     GTEST_SKIP() << "Failed to find/create "
75                  << invalid_host_artifacts_path + "/bin";
76   }
77 
78   // group_name : "meow"
79   auto result_bad_home =
80       db.AddInstanceGroup({.group_name = "meow",
81                            .home_dir = "/path/to/never/exists",
82                            .host_artifacts_path = HostArtifactsPath(),
83                            .product_out_path = HostArtifactsPath()});
84   auto result_bad_host_bin_dir =
85       db.AddInstanceGroup({.group_name = "meow",
86                            .home_dir = home,
87                            .host_artifacts_path = "/path/to/never/exists",
88                            .product_out_path = "/path/to/never/exists"});
89   auto result_both_bad =
90       db.AddInstanceGroup({.group_name = "meow",
91                            .home_dir = "/path/to/never/exists",
92                            .host_artifacts_path = "/path/to/never/exists",
93                            .product_out_path = "/path/to/never/exists"});
94   auto result_bad_group_name =
95       db.AddInstanceGroup({.group_name = "0invalid_group_name",
96                            .home_dir = home,
97                            .host_artifacts_path = HostArtifactsPath(),
98                            .product_out_path = HostArtifactsPath()});
99   // Everything is correct but one thing: the host artifacts directory does not
100   // have host tool files such as launch_cvd
101   auto result_non_qualifying_host_tool_dir =
102       db.AddInstanceGroup({.group_name = "meow",
103                            .home_dir = home,
104                            .host_artifacts_path = invalid_host_artifacts_path,
105                            .product_out_path = invalid_host_artifacts_path});
106 
107   ASSERT_FALSE(result_bad_home.ok());
108   ASSERT_FALSE(result_bad_host_bin_dir.ok());
109   ASSERT_FALSE(result_both_bad.ok());
110   ASSERT_FALSE(result_bad_group_name.ok());
111   ASSERT_FALSE(result_non_qualifying_host_tool_dir.ok());
112 }
113 
TEST_F(CvdInstanceDatabaseTest,AddWithValidGroupInfo)114 TEST_F(CvdInstanceDatabaseTest, AddWithValidGroupInfo) {
115   if (!SetUpOk()) {
116     GTEST_SKIP() << Error().msg;
117   }
118   auto& db = GetDb();
119   const std::string home0{Workspace() + "/" + "home0"};
120   if (!EnsureDirectoryExists(home0).ok()) {
121     GTEST_SKIP() << "Failed to find/create " << home0;
122   }
123   const std::string home1{Workspace() + "/" + "home1"};
124   if (!EnsureDirectoryExists(home1).ok()) {
125     GTEST_SKIP() << "Failed to find/create " << home1;
126   }
127 
128   ASSERT_TRUE(db.AddInstanceGroup({.group_name = "meow",
129                                    .home_dir = home0,
130                                    .host_artifacts_path = HostArtifactsPath(),
131                                    .product_out_path = HostArtifactsPath()})
132                   .ok());
133   ASSERT_TRUE(db.AddInstanceGroup({.group_name = "miaou",
134                                    .home_dir = home1,
135                                    .host_artifacts_path = HostArtifactsPath(),
136                                    .product_out_path = HostArtifactsPath()})
137                   .ok());
138 }
139 
TEST_F(CvdInstanceDatabaseTest,AddToTakenHome)140 TEST_F(CvdInstanceDatabaseTest, AddToTakenHome) {
141   if (!SetUpOk()) {
142     GTEST_SKIP() << Error().msg;
143   }
144   auto& db = GetDb();
145   const std::string home{Workspace() + "/" + "my_home"};
146   if (!EnsureDirectoryExists(home).ok()) {
147     GTEST_SKIP() << "Failed to find/create " << home;
148   }
149 
150   ASSERT_TRUE(db.AddInstanceGroup({.group_name = "meow",
151                                    .home_dir = home,
152                                    .host_artifacts_path = HostArtifactsPath(),
153                                    .product_out_path = HostArtifactsPath()})
154                   .ok());
155   ASSERT_FALSE(db.AddInstanceGroup({.group_name = "meow",
156                                     .home_dir = home,
157                                     .host_artifacts_path = HostArtifactsPath(),
158                                     .product_out_path = HostArtifactsPath()})
159                    .ok());
160 }
161 
TEST_F(CvdInstanceDatabaseTest,Clear)162 TEST_F(CvdInstanceDatabaseTest, Clear) {
163   /* AddGroups(name):
164    *   HOME: Workspace() + "/" + name
165    *   HostArtifactsPath: Workspace() + "/" + "android_host_out"
166    *   group_ := LocalInstanceGroup(name, HOME, HostArtifactsPath)
167    */
168   if (!SetUpOk() || !AddGroups({"nyah", "yah_ong"})) {
169     GTEST_SKIP() << Error().msg;
170   }
171   auto& db = GetDb();
172 
173   // test Clear()
174   ASSERT_FALSE(db.IsEmpty());
175   db.Clear();
176   ASSERT_TRUE(db.IsEmpty());
177 }
178 
TEST_F(CvdInstanceDatabaseTest,SearchGroups)179 TEST_F(CvdInstanceDatabaseTest, SearchGroups) {
180   if (!SetUpOk() || !AddGroups({"myau", "miau"})) {
181     GTEST_SKIP() << Error().msg;
182   }
183   auto& db = GetDb();
184   const std::string valid_home_search_key{Workspace() + "/" + "myau"};
185   const std::string invalid_home_search_key{"/no/such/path"};
186 
187   auto valid_groups = db.FindGroups({kHomeField, valid_home_search_key});
188   auto valid_group = db.FindGroup({kHomeField, valid_home_search_key});
189   auto invalid_groups = db.FindGroups({kHomeField, invalid_home_search_key});
190   auto invalid_group = db.FindGroup({kHomeField, invalid_home_search_key});
191 
192   ASSERT_TRUE(valid_groups.ok());
193   ASSERT_EQ(valid_groups->size(), 1);
194   ASSERT_TRUE(valid_group.ok());
195 
196   ASSERT_TRUE(invalid_groups.ok());
197   ASSERT_EQ(invalid_groups->size(), 0);
198   ASSERT_FALSE(invalid_group.ok());
199 }
200 
TEST_F(CvdInstanceDatabaseTest,RemoveGroup)201 TEST_F(CvdInstanceDatabaseTest, RemoveGroup) {
202   if (!SetUpOk()) {
203     GTEST_SKIP() << Error().msg;
204   }
205   auto& db = GetDb();
206   if (!AddGroups({"miaaaw", "meow", "mjau"})) {
207     GTEST_SKIP() << Error().msg;
208   }
209   auto eng_group = db.FindGroup({kHomeField, Workspace() + "/" + "meow"});
210   if (!eng_group.ok()) {
211     GTEST_SKIP() << "meow"
212                  << " group was not found.";
213   }
214 
215   ASSERT_TRUE(db.RemoveInstanceGroup(*eng_group));
216   ASSERT_FALSE(db.RemoveInstanceGroup(*eng_group));
217 }
218 
TEST_F(CvdInstanceDatabaseTest,AddInstances)219 TEST_F(CvdInstanceDatabaseTest, AddInstances) {
220   if (!SetUpOk() || !AddGroups({"yah_ong"})) {
221     GTEST_SKIP() << Error().msg;
222   }
223   auto& db = GetDb();
224   auto kitty_group = db.FindGroup({kHomeField, Workspace() + "/" + "yah_ong"});
225   if (!kitty_group.ok()) {
226     GTEST_SKIP() << "yah_ong"
227                  << " group was not found";
228   }
229   const auto& instances = kitty_group->Get().Instances();
230 
231   ASSERT_TRUE(db.AddInstance("yah_ong", 1, "yumi").ok());
232   ASSERT_FALSE(db.AddInstance("yah_ong", 3, "yumi").ok());
233   ASSERT_FALSE(db.AddInstance("yah_ong", 1, "tiger").ok());
234   ASSERT_TRUE(db.AddInstance("yah_ong", 3, "tiger").ok());
235   for (auto const& instance_unique_ptr : instances) {
236     ASSERT_TRUE(instance_unique_ptr->PerInstanceName() == "yumi" ||
237                 instance_unique_ptr->PerInstanceName() == "tiger");
238   }
239 }
240 
TEST_F(CvdInstanceDatabaseTest,AddInstancesInvalid)241 TEST_F(CvdInstanceDatabaseTest, AddInstancesInvalid) {
242   if (!SetUpOk() || !AddGroups({"yah_ong"})) {
243     GTEST_SKIP() << Error().msg;
244   }
245   auto& db = GetDb();
246   auto kitty_group = db.FindGroup({kHomeField, Workspace() + "/" + "yah_ong"});
247   if (!kitty_group.ok()) {
248     GTEST_SKIP() << "yah_ong"
249                  << " group was not found";
250   }
251 
252   ASSERT_FALSE(db.AddInstance("yah_ong", 1, "!yumi").ok());
253   ASSERT_FALSE(db.AddInstance("yah_ong", 7, "ti ger").ok());
254 }
255 
TEST_F(CvdInstanceDatabaseTest,FindByInstanceId)256 TEST_F(CvdInstanceDatabaseTest, FindByInstanceId) {
257   // The start of set up
258   if (!SetUpOk()) {
259     GTEST_SKIP() << Error().msg;
260   }
261   if (!AddGroups({"miau", "nyah"})) {
262     GTEST_SKIP() << Error().msg;
263   }
264   auto& db = GetDb();
265   // per_instance_name could be the same if the parent groups are different.
266   std::vector<InstanceInfo> miau_group_instance_id_name_pairs{
267       {1, "8"}, {10, "tv-instance"}};
268   std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{
269       {7, "my_favorite_phone"}, {11, "tv-instance"}, {3, "3_"}};
270   auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
271   auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"});
272   if (!miau_group.ok() || !nyah_group.ok()) {
273     GTEST_SKIP() << "miau or nyah group"
274                  << " group was not found";
275   }
276   if (!AddInstances("miau", miau_group_instance_id_name_pairs) ||
277       !AddInstances("nyah", nyah_group_instance_id_name_pairs)) {
278     GTEST_SKIP() << Error().msg;
279   }
280   // The end of set up
281 
282   auto result1 = db.FindInstance({kInstanceIdField, std::to_string(1)});
283   auto result10 = db.FindInstance({kInstanceIdField, std::to_string(10)});
284   auto result7 = db.FindInstance({kInstanceIdField, std::to_string(7)});
285   auto result11 = db.FindInstance({kInstanceIdField, std::to_string(11)});
286   auto result3 = db.FindInstance({kInstanceIdField, std::to_string(3)});
287   auto result_invalid = db.FindInstance({kInstanceIdField, std::to_string(20)});
288 
289   ASSERT_TRUE(result1.ok());
290   ASSERT_TRUE(result10.ok());
291   ASSERT_TRUE(result7.ok());
292   ASSERT_TRUE(result11.ok());
293   ASSERT_TRUE(result3.ok());
294   ASSERT_EQ(result1->Get().PerInstanceName(), "8");
295   ASSERT_EQ(result10->Get().PerInstanceName(), "tv-instance");
296   ASSERT_EQ(result7->Get().PerInstanceName(), "my_favorite_phone");
297   ASSERT_EQ(result11->Get().PerInstanceName(), "tv-instance");
298   ASSERT_EQ(result3->Get().PerInstanceName(), "3_");
299   ASSERT_FALSE(result_invalid.ok());
300 }
301 
TEST_F(CvdInstanceDatabaseTest,FindByPerInstanceName)302 TEST_F(CvdInstanceDatabaseTest, FindByPerInstanceName) {
303   // starting set up
304   if (!SetUpOk() || !AddGroups({"miau", "nyah"})) {
305     GTEST_SKIP() << Error().msg;
306   }
307   auto& db = GetDb();
308   std::vector<InstanceInfo> miau_group_instance_id_name_pairs{
309       {1, "8"}, {10, "tv_instance"}};
310   std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{
311       {7, "my_favorite_phone"}, {11, "tv_instance"}};
312   auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
313   auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"});
314   if (!miau_group.ok() || !nyah_group.ok()) {
315     GTEST_SKIP() << "miau or nyah "
316                  << " group was not found";
317   }
318   if (!AddInstances("miau", miau_group_instance_id_name_pairs) ||
319       !AddInstances("nyah", nyah_group_instance_id_name_pairs)) {
320     GTEST_SKIP() << Error().msg;
321   }
322   // end of set up
323 
324   auto result1 = db.FindInstance({kInstanceNameField, "8"});
325   auto result10_and_11 = db.FindInstances({kInstanceNameField, "tv_instance"});
326   auto result7 = db.FindInstance({kInstanceNameField, "my_favorite_phone"});
327   auto result_invalid =
328       db.FindInstance({kInstanceNameField, "name_never_seen"});
329 
330   ASSERT_TRUE(result1.ok());
331   ASSERT_TRUE(result10_and_11.ok());
332   ASSERT_TRUE(result7.ok());
333   ASSERT_EQ(result10_and_11->size(), 2);
334   ASSERT_EQ(result1->Get().InstanceId(), 1);
335   ASSERT_EQ(result7->Get().InstanceId(), 7);
336   ASSERT_FALSE(result_invalid.ok());
337 }
338 
TEST_F(CvdInstanceDatabaseTest,FindInstancesByGroupName)339 TEST_F(CvdInstanceDatabaseTest, FindInstancesByGroupName) {
340   // starting set up
341   if (!SetUpOk() || !AddGroups({"miau", "nyah"})) {
342     GTEST_SKIP() << Error().msg;
343   }
344   auto& db = GetDb();
345   std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{
346       {7, "my_favorite_phone"}, {11, "tv_instance"}};
347   auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"});
348   if (!nyah_group.ok()) {
349     GTEST_SKIP() << "nyah group was not found";
350   }
351   if (!AddInstances("nyah", nyah_group_instance_id_name_pairs)) {
352     GTEST_SKIP() << Error().msg;
353   }
354   // end of set up
355 
356   auto result_nyah = db.FindInstances({kGroupNameField, "nyah"});
357   auto result_invalid = db.FindInstance({kGroupNameField, "name_never_seen"});
358 
359   ASSERT_TRUE(result_nyah.ok());
360   std::set<std::string> nyah_instance_names;
361   for (const auto& instance : *result_nyah) {
362     nyah_instance_names.insert(instance.Get().PerInstanceName());
363   }
364   std::set<std::string> expected{"my_favorite_phone", "tv_instance"};
365   ASSERT_EQ(nyah_instance_names, expected);
366   ASSERT_FALSE(result_invalid.ok());
367 }
368 
TEST_F(CvdInstanceDatabaseTest,FindGroupByPerInstanceName)369 TEST_F(CvdInstanceDatabaseTest, FindGroupByPerInstanceName) {
370   // starting set up
371   if (!SetUpOk() || !AddGroups({"miau", "nyah"})) {
372     GTEST_SKIP() << Error().msg;
373   }
374   auto& db = GetDb();
375   std::vector<InstanceInfo> miau_group_instance_id_name_pairs{
376       {1, "8"}, {10, "tv_instance"}};
377   std::vector<InstanceInfo> nyah_group_instance_id_name_pairs{
378       {7, "my_favorite_phone"}, {11, "tv_instance"}};
379   auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
380   auto nyah_group = db.FindGroup({kHomeField, Workspace() + "/" + "nyah"});
381   if (!miau_group.ok() || !nyah_group.ok()) {
382     GTEST_SKIP() << "miau or nyah "
383                  << " group was not found";
384   }
385   if (!AddInstances("miau", miau_group_instance_id_name_pairs) ||
386       !AddInstances("nyah", nyah_group_instance_id_name_pairs)) {
387     GTEST_SKIP() << Error().msg;
388   }
389   // end of set up
390 
391   auto result_miau = db.FindGroups({kInstanceNameField, "8"});
392   auto result_both = db.FindGroups({kInstanceNameField, "tv_instance"});
393   auto result_nyah = db.FindGroups({kInstanceNameField, "my_favorite_phone"});
394   auto result_invalid = db.FindGroups({kInstanceNameField, "name_never_seen"});
395 
396   ASSERT_TRUE(result_miau.ok());
397   ASSERT_TRUE(result_both.ok());
398   ASSERT_TRUE(result_nyah.ok());
399   ASSERT_TRUE(result_invalid.ok());
400   ASSERT_EQ(result_miau->size(), 1);
401   ASSERT_EQ(result_both->size(), 2);
402   ASSERT_EQ(result_nyah->size(), 1);
403   ASSERT_TRUE(result_invalid->empty())
404       << "result_invalid should be empty but with size: "
405       << result_invalid->size();
406 }
407 
TEST_F(CvdInstanceDatabaseTest,AddInstancesTogether)408 TEST_F(CvdInstanceDatabaseTest, AddInstancesTogether) {
409   // starting set up
410   if (!SetUpOk() || !AddGroups({"miau"})) {
411     GTEST_SKIP() << Error().msg;
412   }
413   auto& db = GetDb();
414   std::vector<InstanceDatabase::InstanceInfo> miau_group_instance_id_name_pairs{
415       {1, "8"}, {10, "tv_instance"}};
416   auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
417   if (!miau_group.ok()) {
418     GTEST_SKIP() << "miau group was not found";
419   }
420 
421   auto add_result = db.AddInstances("miau", miau_group_instance_id_name_pairs);
422   ASSERT_TRUE(add_result.ok()) << add_result.error().Trace();
423 
424   auto result_8 = db.FindInstance({kInstanceNameField, "8"});
425   auto result_tv = db.FindInstance({kInstanceNameField, "tv_instance"});
426 
427   ASSERT_TRUE(result_8.ok()) << result_8.error().Trace();
428   ASSERT_TRUE(result_tv.ok()) << result_tv.error().Trace();
429 }
430 
TEST_F(CvdInstanceDatabaseJsonTest,DumpLoadDumpCompare)431 TEST_F(CvdInstanceDatabaseJsonTest, DumpLoadDumpCompare) {
432   // starting set up
433   if (!SetUpOk() || !AddGroups({"miau"})) {
434     GTEST_SKIP() << Error().msg;
435   }
436   auto& db = GetDb();
437   std::vector<InstanceDatabase::InstanceInfo> miau_group_instance_id_name_pairs{
438       {1, "8"}, {10, "tv_instance"}};
439   auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
440   if (!miau_group.ok()) {
441     GTEST_SKIP() << "miau group was not found";
442   }
443   auto add_result = db.AddInstances("miau", miau_group_instance_id_name_pairs);
444   if (!add_result.ok()) {
445     GTEST_SKIP() << "Adding instances are not being tested in this test case.";
446   }
447 
448   /*
449    * Dumping to json, clearing up the DB, loading from the json,
450    *
451    */
452   auto serialized_db = db.Serialize();
453   if (!db.RemoveInstanceGroup("miau")) {
454     // not testing RemoveInstanceGroup
455     GTEST_SKIP() << "miau had to be added.";
456   }
457   auto json_parsing = ParseJson(serialized_db.toStyledString());
458   ASSERT_TRUE(json_parsing.ok()) << serialized_db << std::endl
459                                  << " is not a valid json.";
460   auto load_result = db.LoadFromJson(serialized_db);
461   ASSERT_TRUE(load_result.ok()) << load_result.error().Trace();
462   {
463     // re-look up the group and the instances
464     auto miau_group = db.FindGroup({kHomeField, Workspace() + "/" + "miau"});
465     ASSERT_TRUE(miau_group.ok()) << miau_group.error().Trace();
466     auto result_8 = db.FindInstance({kInstanceNameField, "8"});
467     auto result_tv = db.FindInstance({kInstanceNameField, "tv_instance"});
468 
469     ASSERT_TRUE(result_8.ok()) << result_8.error().Trace();
470     ASSERT_TRUE(result_tv.ok()) << result_tv.error().Trace();
471   }
472 }
473 
474 }  // namespace selector
475 }  // namespace cuttlefish
476