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