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 "host/commands/cvd/unittests/selector/instance_database_helper.h"
17
18 #include <algorithm>
19 #include <cstdio>
20 #include <cstdlib>
21
22 #include <android-base/file.h>
23
24 #include "common/libs/fs/shared_buf.h"
25 #include "common/libs/fs/shared_fd.h"
26 #include "common/libs/utils/environment.h"
27 #include "common/libs/utils/files.h"
28 #include "host/commands/cvd/selector/selector_constants.h"
29
30 namespace cuttlefish {
31 namespace selector {
32 namespace {
33
34 // mktemp with /tmp/<subdir>.XXXXXX, and if failed,
35 // mkdir -p /tmp/<subdir>.<default_suffix>
CreateTempDirectory(const std::string & subdir,const std::string & default_suffix)36 std::optional<std::string> CreateTempDirectory(
37 const std::string& subdir, const std::string& default_suffix) {
38 std::string path_pattern = "/tmp/" + subdir + ".XXXXXX";
39 auto ptr = mkdtemp(path_pattern.data());
40 if (ptr) {
41 return {std::string(ptr)};
42 }
43 std::string default_path = "/tmp/" + subdir + "." + default_suffix;
44 return (EnsureDirectoryExists(default_path).ok() ? std::optional(default_path)
45 : std::nullopt);
46 }
47
48 // Linux "touch" a(n empty) file
Touch(const std::string & full_path)49 bool Touch(const std::string& full_path) {
50 // this file is required only to make FileExists() true.
51 SharedFD new_file = SharedFD::Creat(full_path, S_IRUSR | S_IWUSR);
52 return new_file->IsOpen();
53 }
54
55 } // namespace
56
CvdInstanceDatabaseTest()57 CvdInstanceDatabaseTest::CvdInstanceDatabaseTest()
58 : error_{.error_code = ErrorCode::kOk, .msg = ""} {
59 InitWorkspace() && InitMockAndroidHostOut();
60 }
61
~CvdInstanceDatabaseTest()62 CvdInstanceDatabaseTest::~CvdInstanceDatabaseTest() { ClearWorkspace(); }
63
ClearWorkspace()64 void CvdInstanceDatabaseTest::ClearWorkspace() {
65 if (!workspace_dir_.empty()) {
66 RecursivelyRemoveDirectory(workspace_dir_);
67 }
68 }
69
SetErrorCode(const ErrorCode error_code,const std::string & msg)70 void CvdInstanceDatabaseTest::SetErrorCode(const ErrorCode error_code,
71 const std::string& msg) {
72 error_.error_code = error_code;
73 error_.msg = msg;
74 }
75
InitWorkspace()76 bool CvdInstanceDatabaseTest::InitWorkspace() {
77 // creating a parent dir of the mock home directories for each fake group
78 auto result_opt = CreateTempDirectory("cf_unittest", "default_location");
79 if (!result_opt) {
80 SetErrorCode(ErrorCode::kFileError, "Failed to create workspace");
81 return false;
82 }
83 workspace_dir_ = std::move(result_opt.value());
84 return true;
85 }
86
InitMockAndroidHostOut()87 bool CvdInstanceDatabaseTest::InitMockAndroidHostOut() {
88 /* creating a fake host out directory
89 *
90 * As the automated testing system does not guarantee that there is either
91 * ANDROID_HOST_OUT or ".", where we can find host tools, we create a fake
92 * host tool directory just enough to deceive InstanceDatabase APIs.
93 *
94 */
95 std::string android_host_out = workspace_dir_ + "/android_host_out";
96 if (!EnsureDirectoryExists(android_host_out).ok()) {
97 SetErrorCode(ErrorCode::kFileError, "Failed to create " + android_host_out);
98 return false;
99 }
100 android_artifacts_path_ = android_host_out;
101 if (!EnsureDirectoryExists(android_artifacts_path_ + "/bin").ok()) {
102 SetErrorCode(ErrorCode::kFileError,
103 "Failed to create " + android_artifacts_path_ + "/bin");
104 return false;
105 }
106 if (!Touch(android_artifacts_path_ + "/bin" + "/launch_cvd")) {
107 SetErrorCode(ErrorCode::kFileError, "Failed to create mock launch_cvd");
108 return false;
109 }
110 return true;
111 }
112
113 // Add an InstanceGroups with each home directory and android_host_out_
AddGroups(const std::unordered_set<std::string> & base_names)114 bool CvdInstanceDatabaseTest::AddGroups(
115 const std::unordered_set<std::string>& base_names) {
116 for (const auto& base_name : base_names) {
117 const std::string home(Workspace() + "/" + base_name);
118 if (!EnsureDirectoryExists(home).ok()) {
119 SetErrorCode(ErrorCode::kFileError, home + " directory is not found.");
120 return false;
121 }
122 InstanceDatabase::AddInstanceGroupParam param{
123 .group_name = base_name,
124 .home_dir = home,
125 .host_artifacts_path = android_artifacts_path_,
126 .product_out_path = android_artifacts_path_};
127 if (!db_.AddInstanceGroup(param).ok()) {
128 SetErrorCode(ErrorCode::kInstanceDabaseError, "Failed to add group");
129 return false;
130 }
131 }
132 return true;
133 }
134
AddInstances(const std::string & group_name,const std::vector<InstanceInfo> & instances_info)135 bool CvdInstanceDatabaseTest::AddInstances(
136 const std::string& group_name,
137 const std::vector<InstanceInfo>& instances_info) {
138 for (const auto& [id, per_instance_name] : instances_info) {
139 if (!db_.AddInstance(group_name, id, per_instance_name).ok()) {
140 SetErrorCode(ErrorCode::kInstanceDabaseError,
141 "Failed to add instance " + per_instance_name);
142 return false;
143 }
144 }
145 return true;
146 }
147
148 } // namespace selector
149 } // namespace cuttlefish
150