• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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