1 /*
2 * Copyright (C) 2021 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
17 #include <android-base/stringprintf.h>
18 #include <android-base/strings.h>
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
21 #include <vintf/VintfObjectRecovery.h>
22 #include <vintf/parse_string.h>
23
24 #include "constants-private.h"
25 #include "test_constants.h"
26 #include "utils-fake.h"
27
28 using android::base::ConsumePrefix;
29 using android::base::StringPrintf;
30 using testing::_;
31 using testing::Combine;
32 using testing::Invoke;
33 using testing::IsEmpty;
34 using testing::Mock;
35 using testing::NiceMock;
36 using testing::StartsWith;
37 using testing::StrEq;
38 using testing::TestParamInfo;
39 using testing::UnorderedElementsAre;
40 using testing::ValuesIn;
41
42 namespace android::vintf::testing {
43
44 using details::kSystemManifest;
45 using details::kSystemManifestFragmentDir;
46 using details::MockFileSystemWithError;
47 using details::MockPropertyFetcher;
48 using details::MockRuntimeInfo;
49 using details::MockRuntimeInfoFactory;
50
51 template <typename T>
52 using StatusOr = std::variant<status_t, T>;
53
54 using DirectoryContent = std::map<std::string, StatusOr<std::string>>;
55
56 using OptionalType = std::optional<SchemaType>;
OptionalTypes()57 std::vector<OptionalType> OptionalTypes() {
58 return {std::nullopt, SchemaType::DEVICE, SchemaType::FRAMEWORK};
59 }
60
OptionalTypeToString(const OptionalType & optionalType)61 std::string OptionalTypeToString(const OptionalType& optionalType) {
62 if (!optionalType.has_value()) return "broken";
63 return to_string(*optionalType);
64 }
65
66 constexpr const char* kMainFmt = R"(<manifest %s type="%s">
67 <hal format="aidl">
68 <name>android.hardware.main</name>
69 <fqname>IMain/default</fqname>
70 </hal>
71 </manifest>
72 )";
73
74 constexpr const char* kFragment1Fmt = R"(<manifest %s type="%s">
75 <hal format="aidl">
76 <name>android.hardware.fragment1</name>
77 <fqname>IFragment/default</fqname>
78 </hal>
79 </manifest>
80 )";
81
82 constexpr const char* kFragment2Fmt = R"(<manifest %s type="%s">
83 <hal format="aidl">
84 <name>android.hardware.fragment2</name>
85 <fqname>IFragment/default</fqname>
86 </hal>
87 </manifest>
88 )";
89
formatManifest(const char * fmt,const OptionalType & optionalType)90 std::string formatManifest(const char* fmt, const OptionalType& optionalType) {
91 if (!optionalType.has_value()) {
92 return "(broken manifest)";
93 }
94 return StringPrintf(fmt, kMetaVersionStr.c_str(), to_string(*optionalType).c_str());
95 }
96
97 using VintfObjectRecoveryTestParam = std::tuple<OptionalType, OptionalType, OptionalType>;
98 class VintfObjectRecoveryTest : public ::testing::TestWithParam<VintfObjectRecoveryTestParam> {
99 public:
SetUp()100 virtual void SetUp() {
101 vintfObject = VintfObjectRecovery::Builder()
102 .setFileSystem(std::make_unique<NiceMock<MockFileSystemWithError>>())
103 .setRuntimeInfoFactory(std::make_unique<NiceMock<MockRuntimeInfoFactory>>(
104 std::make_shared<NiceMock<MockRuntimeInfo>>()))
105 .setPropertyFetcher(std::make_unique<NiceMock<MockPropertyFetcher>>())
106 .build<VintfObjectRecovery>();
107 auto [mainType, fragType1, fragType2] = GetParam();
108 main = formatManifest(kMainFmt, mainType);
109 frag1 = formatManifest(kFragment1Fmt, fragType1);
110 frag2 = formatManifest(kFragment2Fmt, fragType2);
111 }
TearDown()112 virtual void TearDown() { Mock::VerifyAndClear(&fs()); }
113
fs()114 MockFileSystemWithError& fs() {
115 return static_cast<MockFileSystemWithError&>(*vintfObject->getFileSystem());
116 }
117
setUpManifests(const StatusOr<std::string> & mainContent,const StatusOr<DirectoryContent> & frags)118 void setUpManifests(const StatusOr<std::string>& mainContent,
119 const StatusOr<DirectoryContent>& frags) {
120 // By default, no files exist in the file system.
121 ON_CALL(fs(), listFiles(_, _, _)).WillByDefault(Return(NAME_NOT_FOUND));
122 ON_CALL(fs(), fetch(_, _, _))
123 .WillByDefault(Invoke([](const auto& path, auto*, auto* error) {
124 if (error != nullptr) {
125 *error = "fetch " + path + ": cannot be found on empty filesystem: " +
126 statusToString(NAME_NOT_FOUND);
127 }
128 return NAME_NOT_FOUND;
129 }));
130 ON_CALL(fs(), fetch(StrEq(kSystemManifest), _, _))
131 .WillByDefault(Invoke([=](const auto& path, auto* content, auto* error) -> status_t {
132 if (std::holds_alternative<status_t>(mainContent)) {
133 if (error != nullptr) {
134 *error = "fetch " + path + ": set to return " +
135 statusToString(std::get<status_t>(mainContent));
136 }
137 return std::get<status_t>(mainContent);
138 }
139 *content = std::get<std::string>(mainContent);
140 return OK;
141 }));
142 ON_CALL(fs(), listFiles(StrEq(kSystemManifestFragmentDir), _, _))
143 .WillByDefault(Invoke([=](const std::string& path, std::vector<std::string>* out,
144 auto* error) -> status_t {
145 if (std::holds_alternative<status_t>(frags)) {
146 if (error != nullptr) {
147 *error = "list " + path + ": set to return " +
148 statusToString(std::get<status_t>(frags));
149 }
150 return std::get<status_t>(frags);
151 }
152 for (const auto& [name, statusOrFile] : std::get<DirectoryContent>(frags)) {
153 out->push_back(name);
154 }
155 return OK;
156 }));
157 ON_CALL(fs(), fetch(StartsWith(kSystemManifestFragmentDir), _, _))
158 .WillByDefault(Invoke([=](const auto& path, auto* content, auto* error) -> status_t {
159 if (std::holds_alternative<status_t>(frags)) {
160 if (error != nullptr) {
161 *error = "fetch " + path + ": for dir, set to return " +
162 statusToString(std::get<status_t>(frags));
163 }
164 return std::get<status_t>(frags);
165 }
166 const auto& directoryContent = std::get<DirectoryContent>(frags);
167 std::string_view subpath = path;
168 bool consumed = ConsumePrefix(&subpath, kSystemManifestFragmentDir);
169 EXPECT_TRUE(consumed)
170 << path << " does not start with " << kSystemManifestFragmentDir;
171 auto it = directoryContent.find(std::string(subpath));
172 if (it == directoryContent.end()) {
173 if (error != nullptr) {
174 *error = "fetch " + path +
175 ": not in DirectoryContent: " + statusToString(NAME_NOT_FOUND);
176 }
177 return NAME_NOT_FOUND;
178 }
179
180 const auto& [name, statusOrFile] = *it;
181 if (std::holds_alternative<status_t>(statusOrFile)) {
182 *error = "fetch " + path + ": for file, set to return " +
183 statusToString(std::get<status_t>(statusOrFile));
184 return std::get<status_t>(statusOrFile);
185 }
186 *content = std::get<std::string>(statusOrFile);
187 return OK;
188 }));
189 }
190
ParamToString(const TestParamInfo<ParamType> & info)191 static std::string ParamToString(const TestParamInfo<ParamType>& info) {
192 auto [mainType, fragType1, fragType2] = info.param;
193 auto s = "main_" + OptionalTypeToString(mainType);
194 s += "_frag1_" + OptionalTypeToString(fragType1);
195 s += "_frag2_" + OptionalTypeToString(fragType2);
196 return s;
197 }
198
199 std::unique_ptr<VintfObjectRecovery> vintfObject;
200 std::string main;
201 std::string frag1;
202 std::string frag2;
203 };
204
TEST_P(VintfObjectRecoveryTest,Empty)205 TEST_P(VintfObjectRecoveryTest, Empty) {
206 setUpManifests(NAME_NOT_FOUND, NAME_NOT_FOUND);
207 auto manifest = vintfObject->getRecoveryHalManifest();
208 ASSERT_NE(nullptr, manifest);
209 auto hals = manifest->getHalNames();
210 EXPECT_THAT(hals, IsEmpty());
211 }
212
TEST_P(VintfObjectRecoveryTest,InaccessibleMainManifest)213 TEST_P(VintfObjectRecoveryTest, InaccessibleMainManifest) {
214 setUpManifests(UNKNOWN_ERROR, NAME_NOT_FOUND);
215 auto manifest = vintfObject->getRecoveryHalManifest();
216 EXPECT_EQ(nullptr, manifest);
217 }
218
TEST_P(VintfObjectRecoveryTest,MainManifestOnly)219 TEST_P(VintfObjectRecoveryTest, MainManifestOnly) {
220 auto [mainType, fragType1, fragType2] = GetParam();
221 setUpManifests(main, NAME_NOT_FOUND);
222 auto manifest = vintfObject->getRecoveryHalManifest();
223 if (!mainType.has_value()) { // main manifest is broken
224 EXPECT_EQ(nullptr, manifest);
225 return;
226 }
227 ASSERT_NE(nullptr, manifest);
228 EXPECT_THAT(manifest->getHalNames(), UnorderedElementsAre("android.hardware.main"));
229 }
230
TEST_P(VintfObjectRecoveryTest,MainManifestAndDirectoryOnly)231 TEST_P(VintfObjectRecoveryTest, MainManifestAndDirectoryOnly) {
232 auto [mainType, fragType1, fragType2] = GetParam();
233 setUpManifests(main, {});
234 auto manifest = vintfObject->getRecoveryHalManifest();
235 if (!mainType.has_value()) { // main manifest is broken
236 EXPECT_EQ(nullptr, manifest);
237 return;
238 }
239 ASSERT_NE(nullptr, manifest);
240 EXPECT_THAT(manifest->getHalNames(), UnorderedElementsAre("android.hardware.main"));
241 }
242
TEST_P(VintfObjectRecoveryTest,MainManifestAndInaccessibleFragment)243 TEST_P(VintfObjectRecoveryTest, MainManifestAndInaccessibleFragment) {
244 setUpManifests(main, DirectoryContent{{"frag1.xml", UNKNOWN_ERROR}});
245 auto manifest = vintfObject->getRecoveryHalManifest();
246 EXPECT_EQ(nullptr, manifest);
247 }
248
TEST_P(VintfObjectRecoveryTest,MainManifestAndFragments)249 TEST_P(VintfObjectRecoveryTest, MainManifestAndFragments) {
250 auto [mainType, fragType1, fragType2] = GetParam();
251 setUpManifests(main, DirectoryContent{{"frag1.xml", frag1}, {"frag2.xml", frag2}});
252 auto manifest = vintfObject->getRecoveryHalManifest();
253 if (!mainType.has_value() || !fragType1.has_value() || !fragType2.has_value()) {
254 // some manifest(s) are broken
255 EXPECT_EQ(nullptr, manifest);
256 return;
257 }
258 ASSERT_NE(nullptr, manifest);
259 EXPECT_THAT(manifest->getHalNames(),
260 UnorderedElementsAre("android.hardware.main", "android.hardware.fragment1",
261 "android.hardware.fragment2"));
262 }
263
TEST_P(VintfObjectRecoveryTest,InaccessibleDirectory)264 TEST_P(VintfObjectRecoveryTest, InaccessibleDirectory) {
265 setUpManifests(NAME_NOT_FOUND, UNKNOWN_ERROR);
266 auto manifest = vintfObject->getRecoveryHalManifest();
267 EXPECT_EQ(nullptr, manifest);
268 }
269
TEST_P(VintfObjectRecoveryTest,InaccessibleFragment)270 TEST_P(VintfObjectRecoveryTest, InaccessibleFragment) {
271 setUpManifests(NAME_NOT_FOUND, DirectoryContent{{"frag1.xml", UNKNOWN_ERROR}});
272 auto manifest = vintfObject->getRecoveryHalManifest();
273 EXPECT_EQ(nullptr, manifest);
274 }
275
TEST_P(VintfObjectRecoveryTest,SomeInaccessibleFragment)276 TEST_P(VintfObjectRecoveryTest, SomeInaccessibleFragment) {
277 setUpManifests(NAME_NOT_FOUND,
278 DirectoryContent{{"frag1.xml", UNKNOWN_ERROR}, {"frag2.xml", frag2}});
279 auto manifest = vintfObject->getRecoveryHalManifest();
280 EXPECT_EQ(nullptr, manifest);
281 }
282
TEST_P(VintfObjectRecoveryTest,DirectoryOnly)283 TEST_P(VintfObjectRecoveryTest, DirectoryOnly) {
284 setUpManifests(NAME_NOT_FOUND, {});
285 auto manifest = vintfObject->getRecoveryHalManifest();
286 ASSERT_NE(nullptr, manifest);
287 EXPECT_THAT(manifest->getHalNames(), IsEmpty());
288 }
289
TEST_P(VintfObjectRecoveryTest,FragmentsOnly)290 TEST_P(VintfObjectRecoveryTest, FragmentsOnly) {
291 auto [mainType, fragType1, fragType2] = GetParam();
292 setUpManifests(NAME_NOT_FOUND, DirectoryContent{{"frag1.xml", frag1}, {"frag2.xml", frag2}});
293 auto manifest = vintfObject->getRecoveryHalManifest();
294 if (!fragType1.has_value() || !fragType2.has_value()) {
295 // some manifest(s) are broken
296 EXPECT_EQ(nullptr, manifest);
297 return;
298 }
299 ASSERT_NE(nullptr, manifest);
300 EXPECT_THAT(manifest->getHalNames(),
301 UnorderedElementsAre("android.hardware.fragment1", "android.hardware.fragment2"));
302 }
303
304 INSTANTIATE_TEST_CASE_P(VintfObjectRecoveryTest, VintfObjectRecoveryTest,
305 Combine(ValuesIn(OptionalTypes()), ValuesIn(OptionalTypes()),
306 ValuesIn(OptionalTypes())),
307 VintfObjectRecoveryTest::ParamToString);
308
309 } // namespace android::vintf::testing
310