diff options
-rw-r--r-- | CompatibilityMatrix.cpp | 81 | ||||
-rw-r--r-- | include/vintf/CompatibilityMatrix.h | 3 | ||||
-rw-r--r-- | test/vintf_object_tests.cpp | 94 |
3 files changed, 170 insertions, 8 deletions
diff --git a/CompatibilityMatrix.cpp b/CompatibilityMatrix.cpp index 451ca60..338e5be 100644 --- a/CompatibilityMatrix.cpp +++ b/CompatibilityMatrix.cpp | |||
@@ -194,6 +194,36 @@ bool CompatibilityMatrix::addAllXmlFilesAsOptional(CompatibilityMatrix* other, s | |||
194 | return true; | 194 | return true; |
195 | } | 195 | } |
196 | 196 | ||
197 | bool CompatibilityMatrix::addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error) { | ||
198 | if (other == nullptr || other->level() <= level()) { | ||
199 | return true; | ||
200 | } | ||
201 | |||
202 | for (MatrixKernel& kernelToAdd : other->framework.mKernels) { | ||
203 | bool exists = | ||
204 | std::any_of(this->framework.mKernels.begin(), this->framework.mKernels.end(), | ||
205 | [&kernelToAdd](const MatrixKernel& existing) { | ||
206 | return kernelToAdd.minLts().version == existing.minLts().version && | ||
207 | kernelToAdd.minLts().majorRev == existing.minLts().majorRev; | ||
208 | }); | ||
209 | |||
210 | if (exists) { | ||
211 | // Shouldn't retroactively add requirements to minLts(), so ignore this. | ||
212 | // This happens even when kernelToAdd.conditions() != existing.conditions(). | ||
213 | continue; | ||
214 | } | ||
215 | |||
216 | KernelVersion minLts = kernelToAdd.minLts(); | ||
217 | if (!add(std::move(kernelToAdd))) { | ||
218 | if (error) { | ||
219 | *error = "Cannot add " + to_string(minLts) + " for unknown reason."; | ||
220 | } | ||
221 | return false; | ||
222 | } | ||
223 | } | ||
224 | return true; | ||
225 | } | ||
226 | |||
197 | bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) { | 227 | bool operator==(const CompatibilityMatrix &lft, const CompatibilityMatrix &rgt) { |
198 | return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals && | 228 | return lft.mType == rgt.mType && lft.mLevel == rgt.mLevel && lft.mHals == rgt.mHals && |
199 | lft.mXmlFiles == rgt.mXmlFiles && | 229 | lft.mXmlFiles == rgt.mXmlFiles && |
@@ -258,9 +288,32 @@ CompatibilityMatrix* CompatibilityMatrix::findOrInsertBaseMatrix( | |||
258 | return matrix; | 288 | return matrix; |
259 | } | 289 | } |
260 | 290 | ||
291 | // Check if there are two files declaring level="1", for example, because | ||
292 | // combine() use this assumption to simplify a lot of logic. | ||
293 | static bool checkDuplicateLevels(const std::vector<Named<CompatibilityMatrix>>& matrices, | ||
294 | std::string* error) { | ||
295 | std::map<Level, const std::string*> existingLevels; | ||
296 | for (const auto& e : matrices) { | ||
297 | if (e.object.level() == Level::UNSPECIFIED && | ||
298 | existingLevels.find(e.object.level()) != existingLevels.end()) { | ||
299 | if (error) { | ||
300 | *error = "Conflict of levels: file \"" + | ||
301 | *existingLevels.find(e.object.level())->second + "\" and \"" + e.name + | ||
302 | " both have level " + to_string(e.object.level()); | ||
303 | } | ||
304 | return false; | ||
305 | } | ||
306 | existingLevels.emplace(e.object.level(), &e.name); | ||
307 | } | ||
308 | return true; | ||
309 | } | ||
310 | |||
261 | CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel, | 311 | CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel, |
262 | std::vector<Named<CompatibilityMatrix>>* matrices, | 312 | std::vector<Named<CompatibilityMatrix>>* matrices, |
263 | std::string* error) { | 313 | std::string* error) { |
314 | if (!checkDuplicateLevels(*matrices, error)) { | ||
315 | return nullptr; | ||
316 | } | ||
264 | 317 | ||
265 | CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error); | 318 | CompatibilityMatrix* matrix = findOrInsertBaseMatrix(matrices, error); |
266 | if (matrix == nullptr) { | 319 | if (matrix == nullptr) { |
@@ -312,6 +365,13 @@ CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel, | |||
312 | } | 365 | } |
313 | } | 366 | } |
314 | 367 | ||
368 | // Add <kernel> from exact "level", then optionally add <kernel> from high levels to low levels. | ||
369 | // For example, (each letter is a kernel version x.y.z) | ||
370 | // 1.xml: A1, B1 | ||
371 | // 2.xml: B2, C2, D2 | ||
372 | // 3.xml: D3, E3 | ||
373 | // Then the combined 1.xml should have | ||
374 | // A1, B1 (from 1.xml, required), C2, D2, E3 (optional, use earliest possible). | ||
315 | for (auto& e : *matrices) { | 375 | for (auto& e : *matrices) { |
316 | if (&e.object != matrix && e.object.level() == deviceLevel && | 376 | if (&e.object != matrix && e.object.level() == deviceLevel && |
317 | e.object.type() == SchemaType::FRAMEWORK) { | 377 | e.object.type() == SchemaType::FRAMEWORK) { |
@@ -328,6 +388,27 @@ CompatibilityMatrix* CompatibilityMatrix::combine(Level deviceLevel, | |||
328 | } | 388 | } |
329 | } | 389 | } |
330 | 390 | ||
391 | // There is only one file per level, hence a map is used instead of a multimap. Also, there is | ||
392 | // no good ordering (i.e. priority) for multiple files with the same level. | ||
393 | std::map<Level, Named<CompatibilityMatrix>*> matricesMap; | ||
394 | for (auto& e : *matrices) { | ||
395 | if (&e.object != matrix && e.object.level() != Level::UNSPECIFIED && | ||
396 | e.object.level() > deviceLevel && e.object.type() == SchemaType::FRAMEWORK) { | ||
397 | matricesMap.emplace(e.object.level(), &e); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | for (auto&& pair : matricesMap) { | ||
402 | if (!matrix->addAllKernelsAsOptional(&pair.second->object, error)) { | ||
403 | if (error) { | ||
404 | *error = "Cannot add new kernel versions from FCM version " + | ||
405 | to_string(pair.first) + " (" + pair.second->name + ")" + | ||
406 | " to FCM version " + to_string(deviceLevel) + ": " + *error; | ||
407 | } | ||
408 | return nullptr; | ||
409 | } | ||
410 | } | ||
411 | |||
331 | return matrix; | 412 | return matrix; |
332 | } | 413 | } |
333 | 414 | ||
diff --git a/include/vintf/CompatibilityMatrix.h b/include/vintf/CompatibilityMatrix.h index ff5dd24..a43d493 100644 --- a/include/vintf/CompatibilityMatrix.h +++ b/include/vintf/CompatibilityMatrix.h | |||
@@ -75,6 +75,9 @@ struct CompatibilityMatrix : public HalGroup<MatrixHal>, public XmlFileGroup<Mat | |||
75 | // Similar to addAllHalsAsOptional but on <xmlfile> entries. | 75 | // Similar to addAllHalsAsOptional but on <xmlfile> entries. |
76 | bool addAllXmlFilesAsOptional(CompatibilityMatrix* other, std::string* error); | 76 | bool addAllXmlFilesAsOptional(CompatibilityMatrix* other, std::string* error); |
77 | 77 | ||
78 | // Similar to addAllHalsAsOptional but on <kernel> entries. | ||
79 | bool addAllKernelsAsOptional(CompatibilityMatrix* other, std::string* error); | ||
80 | |||
78 | status_t fetchAllInformation(const std::string& path, std::string* error = nullptr); | 81 | status_t fetchAllInformation(const std::string& path, std::string* error = nullptr); |
79 | 82 | ||
80 | // Combine a subset of "matrices". For each CompatibilityMatrix in matrices, | 83 | // Combine a subset of "matrices". For each CompatibilityMatrix in matrices, |
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp index 48623b8..86d43a4 100644 --- a/test/vintf_object_tests.cpp +++ b/test/vintf_object_tests.cpp | |||
@@ -34,10 +34,12 @@ using namespace ::android::vintf::details; | |||
34 | 34 | ||
35 | using android::FqInstance; | 35 | using android::FqInstance; |
36 | 36 | ||
37 | static bool In(const std::string& sub, const std::string& str) { | 37 | static AssertionResult In(const std::string& sub, const std::string& str) { |
38 | return str.find(sub) != std::string::npos; | 38 | return (str.find(sub) != std::string::npos ? AssertionSuccess() : AssertionFailure()) |
39 | << "Value is " << str; | ||
39 | } | 40 | } |
40 | #define EXPECT_IN(sub, str) EXPECT_TRUE(In((sub), (str))) << (str); | 41 | #define EXPECT_IN(sub, str) EXPECT_TRUE(In((sub), (str))) |
42 | #define EXPECT_NOT_IN(sub, str) EXPECT_FALSE(In((sub), (str))) | ||
41 | 43 | ||
42 | // | 44 | // |
43 | // Set of Xml1 metadata compatible with each other. | 45 | // Set of Xml1 metadata compatible with each other. |
@@ -1017,16 +1019,16 @@ TEST_F(DeprecateTest, CheckMajor2) { | |||
1017 | << "major@1.0 should be deprecated. " << error; | 1019 | << "major@1.0 should be deprecated. " << error; |
1018 | } | 1020 | } |
1019 | 1021 | ||
1020 | class RegexTest : public VintfObjectTestBase { | 1022 | class MultiMatrixTest : public VintfObjectTestBase { |
1021 | protected: | 1023 | protected: |
1022 | static std::string getFileName(size_t i) { | 1024 | static std::string getFileName(size_t i) { |
1023 | return "compatibility_matrix." + std::to_string(static_cast<Level>(i)) + ".xml"; | 1025 | return "compatibility_matrix." + std::to_string(static_cast<Level>(i)) + ".xml"; |
1024 | } | 1026 | } |
1025 | virtual void SetUp() override { | 1027 | void SetUpMockSystemMatrices(const std::vector<std::string>& xmls) { |
1026 | EXPECT_CALL(fetcher(), listFiles(StrEq(kSystemVintfDir), _, _)) | 1028 | EXPECT_CALL(fetcher(), listFiles(StrEq(kSystemVintfDir), _, _)) |
1027 | .WillRepeatedly(Invoke([](const auto&, auto* out, auto*) { | 1029 | .WillRepeatedly(Invoke([=](const auto&, auto* out, auto*) { |
1028 | size_t i = 1; | 1030 | size_t i = 1; |
1029 | for (const auto& content : systemMatrixRegexXmls) { | 1031 | for (const auto& content : xmls) { |
1030 | (void)content; | 1032 | (void)content; |
1031 | out->push_back(getFileName(i)); | 1033 | out->push_back(getFileName(i)); |
1032 | ++i; | 1034 | ++i; |
@@ -1034,7 +1036,7 @@ class RegexTest : public VintfObjectTestBase { | |||
1034 | return ::android::OK; | 1036 | return ::android::OK; |
1035 | })); | 1037 | })); |
1036 | size_t i = 1; | 1038 | size_t i = 1; |
1037 | for (const auto& content : systemMatrixRegexXmls) { | 1039 | for (const auto& content : xmls) { |
1038 | expectFetchRepeatedly(kSystemVintfDir + getFileName(i), content); | 1040 | expectFetchRepeatedly(kSystemVintfDir + getFileName(i), content); |
1039 | ++i; | 1041 | ++i; |
1040 | } | 1042 | } |
@@ -1048,6 +1050,11 @@ class RegexTest : public VintfObjectTestBase { | |||
1048 | } | 1050 | } |
1049 | }; | 1051 | }; |
1050 | 1052 | ||
1053 | class RegexTest : public MultiMatrixTest { | ||
1054 | protected: | ||
1055 | virtual void SetUp() { SetUpMockSystemMatrices(systemMatrixRegexXmls); } | ||
1056 | }; | ||
1057 | |||
1051 | TEST_F(RegexTest, CombineLevel1) { | 1058 | TEST_F(RegexTest, CombineLevel1) { |
1052 | expectTargetFcmVersion(1); | 1059 | expectTargetFcmVersion(1); |
1053 | auto matrix = VintfObject::GetFrameworkCompatibilityMatrix(true /* skipCache */); | 1060 | auto matrix = VintfObject::GetFrameworkCompatibilityMatrix(true /* skipCache */); |
@@ -1212,6 +1219,77 @@ TEST_F(RegexTest, DeprecateLevel3) { | |||
1212 | } | 1219 | } |
1213 | } | 1220 | } |
1214 | 1221 | ||
1222 | // | ||
1223 | // Set of framework matrices of different FCM version with <kernel>. | ||
1224 | // | ||
1225 | |||
1226 | #define FAKE_KERNEL(__version__, __key__) \ | ||
1227 | " <kernel version=\"" __version__ "\">\n" \ | ||
1228 | " <config>\n" \ | ||
1229 | " <key>CONFIG_" __key__ "</key>\n" \ | ||
1230 | " <value type=\"tristate\">y</value>\n" \ | ||
1231 | " </config>\n" \ | ||
1232 | " </kernel>\n" | ||
1233 | |||
1234 | const static std::vector<std::string> systemMatrixKernelXmls = { | ||
1235 | // 1.xml | ||
1236 | "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n" | ||
1237 | FAKE_KERNEL("1.0.0", "A1") | ||
1238 | FAKE_KERNEL("2.0.0", "B1") | ||
1239 | "</compatibility-matrix>\n", | ||
1240 | // 2.xml | ||
1241 | "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\n" | ||
1242 | FAKE_KERNEL("2.0.0", "B2") | ||
1243 | FAKE_KERNEL("3.0.0", "C2") | ||
1244 | FAKE_KERNEL("4.0.0", "D2") | ||
1245 | "</compatibility-matrix>\n", | ||
1246 | // 3.xml | ||
1247 | "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"3\">\n" | ||
1248 | FAKE_KERNEL("4.0.0", "D3") | ||
1249 | FAKE_KERNEL("5.0.0", "E3") | ||
1250 | "</compatibility-matrix>\n", | ||
1251 | }; | ||
1252 | |||
1253 | class KernelTest : public MultiMatrixTest {}; | ||
1254 | |||
1255 | // Assume that we are developing level 2. Test that old <kernel> requirements should | ||
1256 | // not change and new <kernel> versions are added. | ||
1257 | TEST_F(KernelTest, Level1AndLevel2) { | ||
1258 | SetUpMockSystemMatrices({systemMatrixKernelXmls[0], systemMatrixKernelXmls[1]}); | ||
1259 | |||
1260 | expectTargetFcmVersion(1); | ||
1261 | auto matrix = VintfObject::GetFrameworkCompatibilityMatrix(true /* skipCache */); | ||
1262 | ASSERT_NE(nullptr, matrix); | ||
1263 | std::string xml = gCompatibilityMatrixConverter(*matrix); | ||
1264 | |||
1265 | EXPECT_IN(FAKE_KERNEL("1.0.0", "A1"), xml) << "\nOld requirements must not change."; | ||
1266 | EXPECT_IN(FAKE_KERNEL("2.0.0", "B1"), xml) << "\nOld requirements must not change."; | ||
1267 | EXPECT_IN(FAKE_KERNEL("3.0.0", "C2"), xml) << "\nShould see <kernel> from new matrices"; | ||
1268 | EXPECT_IN(FAKE_KERNEL("4.0.0", "D2"), xml) << "\nShould see <kernel> from new matrices"; | ||
1269 | |||
1270 | EXPECT_NOT_IN(FAKE_KERNEL("2.0.0", "B2"), xml) << "\nOld requirements must not change"; | ||
1271 | } | ||
1272 | |||
1273 | // Assume that we are developing level 3. Test that old <kernel> requirements should | ||
1274 | // not change and new <kernel> versions are added. | ||
1275 | TEST_F(KernelTest, Level1AndMore) { | ||
1276 | SetUpMockSystemMatrices({systemMatrixKernelXmls}); | ||
1277 | |||
1278 | expectTargetFcmVersion(1); | ||
1279 | auto matrix = VintfObject::GetFrameworkCompatibilityMatrix(true /* skipCache */); | ||
1280 | ASSERT_NE(nullptr, matrix); | ||
1281 | std::string xml = gCompatibilityMatrixConverter(*matrix); | ||
1282 | |||
1283 | EXPECT_IN(FAKE_KERNEL("1.0.0", "A1"), xml) << "\nOld requirements must not change."; | ||
1284 | EXPECT_IN(FAKE_KERNEL("2.0.0", "B1"), xml) << "\nOld requirements must not change."; | ||
1285 | EXPECT_IN(FAKE_KERNEL("3.0.0", "C2"), xml) << "\nOld requirements must not change."; | ||
1286 | EXPECT_IN(FAKE_KERNEL("4.0.0", "D2"), xml) << "\nOld requirements must not change."; | ||
1287 | EXPECT_IN(FAKE_KERNEL("5.0.0", "E3"), xml) << "\nShould see <kernel> from new matrices"; | ||
1288 | |||
1289 | EXPECT_NOT_IN(FAKE_KERNEL("2.0.0", "B2"), xml) << "\nOld requirements must not change"; | ||
1290 | EXPECT_NOT_IN(FAKE_KERNEL("4.0.0", "D3"), xml) << "\nOld requirements must not change"; | ||
1291 | } | ||
1292 | |||
1215 | int main(int argc, char** argv) { | 1293 | int main(int argc, char** argv) { |
1216 | ::testing::InitGoogleMock(&argc, argv); | 1294 | ::testing::InitGoogleMock(&argc, argv); |
1217 | 1295 | ||