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