1 /*
2 * Copyright (C) 2020 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 // This needs to be on top of the file to work.
18 #include "gmock-logging-compat.h"
19
20 #include <sysexits.h>
21
22 #include <filesystem>
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/macros.h>
27 #include <android-base/parseint.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <gtest/gtest.h>
31 #include <vintf/VintfFm.h>
32 #include <vintf/parse_xml.h>
33
34 #include "parse_xml_for_test.h"
35 #include "test_constants.h"
36
37 namespace android::vintf {
38
39 namespace {
40
41 using ::testing::_;
42 using ::testing::Eq;
43 using ::testing::Invoke;
44 using ::testing::MakeMatcher;
45 using ::testing::Matcher;
46 using ::testing::MatcherInterface;
47 using ::testing::MatchResultListener;
48 using ::testing::NiceMock;
49 using ::testing::Return;
50 using ::testing::StartsWith;
51 using ::testing::Test;
52 using ::testing::WithParamInterface;
53
54 using std::string_literals::operator""s;
55
56 static constexpr const char* gFakeRoot = "fake_root";
57 static constexpr const char* gFakeSystemArg = "/system:fake_root/system";
58 static constexpr const char* gBadFakeSystemArg = "/system:fake_root/bad_system";
59 static constexpr const char* gFrameworkManifestPath = "fake_root/system/etc/vintf/manifest.xml";
60 static constexpr const char* gBadFrameworkManifestPath =
61 "fake_root/bad_system/etc/vintf/bad_manifest.xml";
62 static constexpr const char* gFrozenDir = "frozen";
63 static constexpr const char* gFrameworkManifest = R"(
64 <manifest version="2.0" type="framework">
65 <hal>
66 <name>android.frameworks.no_level</name>
67 <transport>hwbinder</transport>
68 <fqname>@1.0::IHidl/default</fqname>
69 </hal>
70 <hal max-level="1">
71 <name>android.frameworks.level1</name>
72 <transport>hwbinder</transport>
73 <fqname>@1.0::IHidl/default</fqname>
74 </hal>
75 <hal max-level="2">
76 <name>android.frameworks.level2</name>
77 <transport>hwbinder</transport>
78 <fqname>@1.0::IHidl/default</fqname>
79 </hal>
80 <hal format="aidl">
81 <name>android.frameworks.no_level</name>
82 <fqname>IAidl/default</fqname>
83 </hal>
84 <hal format="aidl" max-level="1">
85 <name>android.frameworks.level1</name>
86 <fqname>IAidl/default</fqname>
87 </hal>
88 <hal format="aidl" max-level="2">
89 <name>android.frameworks.level2</name>
90 <fqname>IAidl/default</fqname>
91 </hal>
92 </manifest>)";
93 // Missing the android.frameworks.no_level interface.
94 // A missing interface is considered an error
95 static constexpr const char* gBadFrameworkManifest = R"(
96 <manifest version="2.0" type="framework">
97 <hal max-level="1">
98 <name>android.frameworks.level1</name>
99 <transport>hwbinder</transport>
100 <fqname>@1.0::IHidl/default</fqname>
101 </hal>
102 <hal max-level="2">
103 <name>android.frameworks.level2</name>
104 <transport>hwbinder</transport>
105 <fqname>@1.0::IHidl/default</fqname>
106 </hal>
107 <hal format="aidl" max-level="1">
108 <name>android.frameworks.level1</name>
109 <fqname>IAidl/default</fqname>
110 </hal>
111 <hal format="aidl" max-level="2">
112 <name>android.frameworks.level2</name>
113 <fqname>IAidl/default</fqname>
114 </hal>
115 </manifest>)";
116
117 // clang-format off
118 static std::set<std::string> gInstances1 = {
119 "android.frameworks.level1@1.0::IHidl/default",
120 "android.frameworks.level1.IAidl/default (@1)",
121 "android.frameworks.level2@1.0::IHidl/default",
122 "android.frameworks.level2.IAidl/default (@1)",
123 "android.frameworks.no_level@1.0::IHidl/default",
124 "android.frameworks.no_level.IAidl/default (@1)",
125 };
126 static std::set<std::string> gInstances2 = {
127 "android.frameworks.level2@1.0::IHidl/default",
128 "android.frameworks.level2.IAidl/default (@1)",
129 "android.frameworks.no_level@1.0::IHidl/default",
130 "android.frameworks.no_level.IAidl/default (@1)",
131 };
132 static std::set<std::string> gInstances3 = {
133 "android.frameworks.no_level@1.0::IHidl/default",
134 "android.frameworks.no_level.IAidl/default (@1)",
135 };
136 // clang-format on
137
138 class MockWritableFileSystem : public WritableFileSystem {
139 public:
140 MOCK_METHOD(status_t, fetch, (const std::string&, std::string*, std::string*),
141 (const override));
142 MOCK_METHOD(status_t, listFiles, (const std::string&, std::vector<std::string>*, std::string*),
143 (const override));
144 MOCK_METHOD(status_t, modifiedTime, (const std::string&, timespec*, std::string*),
145 (const override));
146 MOCK_METHOD(status_t, write, (const std::string&, const std::string&, std::string*),
147 (const override));
148 MOCK_METHOD(status_t, deleteFile, (const std::string&, std::string*), (const override));
149 };
150
151 // Helper to convert const char* array to char* array.
152 class Args {
153 public:
Args(const std::initializer_list<const char * > & list)154 Args(const std::initializer_list<const char*>& list) {
155 mStrings.reserve(list.size());
156 mBuf.reserve(list.size());
157 for (const char* item : list) {
158 auto& str = mStrings.emplace_back(item);
159 mBuf.push_back(str.data());
160 }
161 }
size()162 int size() { return static_cast<int>(mBuf.size()); }
get()163 char** get() { return mBuf.data(); }
164
165 private:
166 std::vector<std::string> mStrings;
167 std::vector<char*> mBuf;
168 };
169
170 // Returns true if two paths are equivalent. Repeated '/' are omitted.
171 MATCHER_P(PathEq, expected, "") {
172 return std::filesystem::path(arg) == std::filesystem::path(expected);
173 }
174
175 // CHeck if arg contains only the listed instances.
176 class MatrixInstanceMatcher : public MatcherInterface<const std::string&> {
177 public:
MatrixInstanceMatcher(std::set<std::string> expected)178 MatrixInstanceMatcher(std::set<std::string> expected) : mExpected(std::move(expected)) {}
MatchAndExplain(const std::string & actual,MatchResultListener * listener) const179 bool MatchAndExplain(const std::string& actual, MatchResultListener* listener) const override {
180 CompatibilityMatrix actualMatrix;
181 std::string error;
182 if (!fromXml(&actualMatrix, actual, &error)) {
183 *listener << "is not a valid compatibility matrix: " << error;
184 return false;
185 }
186 std::set<std::string> actualInstances;
187 actualMatrix.forEachInstance([&](const auto& matrixInstance) {
188 actualInstances.emplace(
189 matrixInstance.description(matrixInstance.versionRange().minVer()));
190 return true;
191 });
192 if (actualInstances != mExpected) {
193 *listener << "contains instances " << android::base::Join(actualInstances, ",\n");
194 return false;
195 }
196 return true;
197 }
DescribeTo(std::ostream * os) const198 void DescribeTo(std::ostream* os) const override {
199 *os << "contains only the following instances " << android::base::Join(mExpected, ",\n");
200 }
DescribeNegationTo(std::ostream * os) const201 void DescribeNegationTo(std::ostream* os) const override {
202 *os << "does not contain only the following instances "
203 << android::base::Join(mExpected, ",\n");
204 }
205
206 private:
207 std::set<std::string> mExpected;
208 };
209
MatrixContains(std::set<std::string> expected)210 Matcher<const std::string&> MatrixContains(std::set<std::string> expected) {
211 return MakeMatcher(new MatrixInstanceMatcher(expected));
212 }
213
214 } // namespace
215
216 class VintfFmTest : public Test {
217 protected:
SetUp()218 void SetUp() override {
219 auto unique_fs = std::make_unique<NiceMock<MockWritableFileSystem>>();
220 fs = unique_fs.get();
221 vintffm = std::make_unique<VintfFm>(std::move(unique_fs));
222
223 ON_CALL(*fs, fetch(StartsWith(gFakeRoot), _, _)).WillByDefault(Return(NAME_NOT_FOUND));
224 ON_CALL(*fs, fetch(PathEq(gFrameworkManifestPath), _, _))
225 .WillByDefault(Invoke([](const auto&, auto* fetched, auto*) {
226 *fetched = gFrameworkManifest;
227 return OK;
228 }));
229 }
230
expectWriteMatrix(const std::string & path,std::set<std::string> instances)231 void expectWriteMatrix(const std::string& path, std::set<std::string> instances) {
232 EXPECT_CALL(*fs, write(PathEq(path), MatrixContains(std::move(instances)), _))
233 .WillOnce(Return(OK));
234 }
235
236 MockWritableFileSystem* fs;
237 std::unique_ptr<VintfFm> vintffm;
238 };
239
TEST_F(VintfFmTest,Update1)240 TEST_F(VintfFmTest, Update1) {
241 expectWriteMatrix(gFrozenDir + "/1.xml"s, gInstances1);
242
243 Args args({"vintffm", "--update", "--dirmap", gFakeSystemArg, "--level=1", gFrozenDir});
244 EXPECT_EQ(EX_OK, vintffm->main(args.size(), args.get()));
245 }
246
TEST_F(VintfFmTest,Update2)247 TEST_F(VintfFmTest, Update2) {
248 expectWriteMatrix(gFrozenDir + "/2.xml"s, gInstances2);
249
250 Args args({"vintffm", "--update", "--dirmap", gFakeSystemArg, "--level=2", gFrozenDir});
251 EXPECT_EQ(EX_OK, vintffm->main(args.size(), args.get()));
252 }
253
TEST_F(VintfFmTest,Update3)254 TEST_F(VintfFmTest, Update3) {
255 expectWriteMatrix(gFrozenDir + "/3.xml"s, gInstances3);
256
257 Args args({"vintffm", "--update", "--dirmap", gFakeSystemArg, "--level=3", gFrozenDir});
258 EXPECT_EQ(EX_OK, vintffm->main(args.size(), args.get()));
259 }
260
createMatrixHal(HalFormat format,const std::string & package)261 std::string createMatrixHal(HalFormat format, const std::string& package) {
262 std::vector<VersionRange> versionRanges;
263 if (format != HalFormat::AIDL) {
264 versionRanges.emplace_back(1, 0);
265 }
266 auto interface = format == HalFormat::AIDL ? "IAidl" : "IHidl";
267 MatrixHal matrixHal{.format = format,
268 .name = package,
269 .versionRanges = versionRanges,
270 .interfaces = {{interface, HalInterface{interface, {"default"}}}}};
271 return toXml(matrixHal);
272 }
273
274 class VintfFmCheckTest : public VintfFmTest, public WithParamInterface<Level> {
275 protected:
SetUp()276 void SetUp() override {
277 VintfFmTest::SetUp();
278 SetUpFiles();
279 ON_CALL(*fs, listFiles(gFrozenDir, _, _))
280 .WillByDefault(Invoke([this](const auto&, auto* out, auto*) {
281 for (const auto& [path, content] : files) {
282 out->push_back(path);
283 }
284 return OK;
285 }));
286
287 ON_CALL(*fs, fetch(StartsWith(gFrozenDir + "/"s), _, _))
288 .WillByDefault(Invoke([this](const auto& path, auto* fetched, auto*) {
289 auto it = files.find(android::base::Basename(path));
290 if (it == files.end()) {
291 return NAME_NOT_FOUND;
292 }
293 *fetched = it->second;
294 return OK;
295 }));
296 ON_CALL(*fs, fetch(PathEq(gBadFrameworkManifestPath), _, _))
297 .WillByDefault(Invoke([](const auto&, auto* fetched, auto*) {
298 *fetched = gBadFrameworkManifest;
299 return OK;
300 }));
301 }
302
303 std::map<std::string, std::string> files;
304
305 private:
306 // Set up the following files:
307 // 1.xml -> gXml1
308 // ...
309 // |current|.json -> gJson|current|.
310 // |current| is the value of the variable |current|.
311 // |level|.xml contains instances listed in gInstances|level|.
SetUpFiles()312 void SetUpFiles() {
313 auto current = GetParam();
314 auto head = android::base::StringPrintf(R"(<compatibility-matrix %s type="device">)",
315 kMetaVersionStr.c_str());
316 auto tail = R"(</compatibility-matrix>)";
317
318 auto xml3 = createMatrixHal(HalFormat::HIDL, "android.frameworks.no_level") +
319 createMatrixHal(HalFormat::AIDL, "android.frameworks.no_level");
320 auto xml2 = xml3 + createMatrixHal(HalFormat::HIDL, "android.frameworks.level2") +
321 createMatrixHal(HalFormat::AIDL, "android.frameworks.level2");
322 auto xml1 = xml2 + createMatrixHal(HalFormat::HIDL, "android.frameworks.level1") +
323 createMatrixHal(HalFormat::AIDL, "android.frameworks.level1");
324 xml3 = head + xml3 + tail;
325 xml2 = head + xml2 + tail;
326 xml1 = head + xml1 + tail;
327
328 std::map<Level, std::string> allFiles{
329 {static_cast<Level>(1), xml1},
330 {static_cast<Level>(2), xml2},
331 {static_cast<Level>(3), xml3},
332 };
333
334 for (Level level = static_cast<Level>(1); level <= current;
335 level = static_cast<Level>(static_cast<size_t>(level) + 1)) {
336 files.emplace(to_string(level) + ".xml", allFiles[level]);
337 }
338 }
339 };
340
TEST_P(VintfFmCheckTest,Check)341 TEST_P(VintfFmCheckTest, Check) {
342 Args args({"vintffm", "--check", "--dirmap", gFakeSystemArg, gFrozenDir});
343 EXPECT_EQ(EX_OK, vintffm->main(args.size(), args.get()));
344 }
TEST_P(VintfFmCheckTest,CheckMissingManifestHal)345 TEST_P(VintfFmCheckTest, CheckMissingManifestHal) {
346 Args args({"vintffm", "--check", "--dirmap", gBadFakeSystemArg, gFrozenDir});
347 EXPECT_EQ(EX_SOFTWARE, vintffm->main(args.size(), args.get()));
348 }
349
350 INSTANTIATE_TEST_SUITE_P(VintfFmTest, VintfFmCheckTest,
351 ::testing::Values(static_cast<Level>(1), static_cast<Level>(2),
352 static_cast<Level>(3)));
353
354 } // namespace android::vintf
355
main(int argc,char ** argv)356 int main(int argc, char** argv) {
357 // Silence logs on host because they pollute the gtest output. VintfObject writes a lot
358 // of INFO logs.
359 android::base::SetMinimumLogSeverity(android::base::LogSeverity::WARNING);
360 ::testing::InitGoogleMock(&argc, argv);
361 return RUN_ALL_TESTS();
362 }
363