regex-instance: tests for regex instances

Adds tests to libvintf_test and vintf_object_test to test the
following:

* HalManifest::checkUnusedHals
* HalManifest::checkCompatibility
* CompatibilityMatrix::combine
* VintfObject::CheckDeprecation

Bug: 73738616
Test: libvintf_test
Test: vintf_object_test
Test: vts_treble_vintf_test

Change-Id: Ia376d55be3394761d193a044a11229a55944cf9b
Merged-In: Ia376d55be3394761d193a044a11229a55944cf9b
diff --git a/test/LibVintfTest.cpp b/test/LibVintfTest.cpp
index 245cf27..4c7aa23 100644
--- a/test/LibVintfTest.cpp
+++ b/test/LibVintfTest.cpp
@@ -27,6 +27,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/strings.h>
 #include <gtest/gtest.h>
 
 namespace android {
@@ -111,6 +112,9 @@
                                   std::string* e) {
         return cm1->addAllXmlFilesAsOptional(cm2, e);
     }
+    std::set<std::string> checkUnusedHals(const HalManifest& m, const CompatibilityMatrix& cm) {
+        return m.checkUnusedHals(cm);
+    }
 
     std::map<std::string, HalInterface> testHalInterfaces() {
         HalInterface intf("IFoo", {"default"});
@@ -3285,6 +3289,144 @@
     EXPECT_IN("n07 4 v4l1d 1n73rf4c3", error);
 }
 
+TEST_F(LibVintfTest, RegexInstanceValid) {
+    CompatibilityMatrix matrix;
+    std::string error;
+
+    std::string xml =
+        "<compatibility-matrix version=\"1.0\" type=\"framework\">\n"
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.foo</name>\n"
+        "        <version>1.0</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <regex-instance>legacy/[0-9]+</regex-instance>\n"
+        "            <regex-instance>slot[0-9]+</regex-instance>\n"
+        "            <regex-instance>.*</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "</compatibility-matrix>\n";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&matrix, xml, &error)) << error;
+}
+
+TEST_F(LibVintfTest, RegexInstanceInvalid) {
+    CompatibilityMatrix matrix;
+    std::string error;
+    std::string xml =
+        "<compatibility-matrix version=\"1.0\" type=\"framework\">\n"
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.foo</name>\n"
+        "        <version>1.0</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <regex-instance>e{1,2,3}</regex-instance>\n"
+        "            <regex-instance>*</regex-instance>\n"
+        "            <regex-instance>+</regex-instance>\n"
+        "            <regex-instance>[0-9]+</regex-instance>\n"
+        "            <regex-instance>[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "</compatibility-matrix>\n";
+    EXPECT_FALSE(gCompatibilityMatrixConverter(&matrix, xml, &error));
+    EXPECT_IN("Invalid regular expression 'e{1,2,3}'", error);
+    EXPECT_IN("Invalid regular expression '*'", error);
+    EXPECT_IN("Invalid regular expression '+'", error);
+    EXPECT_IN("Duplicated regex-instance '[0-9]+'", error);
+}
+
+TEST_F(LibVintfTest, RegexInstanceCompat) {
+    CompatibilityMatrix matrix;
+    std::string error;
+
+    std::string xml =
+        "<compatibility-matrix version=\"1.0\" type=\"framework\">\n"
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.foo</name>\n"
+        "        <version>1.0</version>\n"
+        "        <version>3.1-2</version>\n"
+        "        <interface>\n"
+        "            <name>IFoo</name>\n"
+        "            <instance>default</instance>\n"
+        "            <regex-instance>legacy/[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n"
+        "    <sepolicy>\n"
+        "        <kernel-sepolicy-version>0</kernel-sepolicy-version>\n"
+        "        <sepolicy-version>0.0</sepolicy-version>\n"
+        "    </sepolicy>\n"
+        "</compatibility-matrix>\n";
+    EXPECT_TRUE(gCompatibilityMatrixConverter(&matrix, xml))
+        << gCompatibilityMatrixConverter.lastError();
+
+    {
+        std::string xml =
+            "<manifest version=\"1.0\" type=\"device\">\n"
+            "    <hal format=\"hidl\">\n"
+            "        <name>android.hardware.foo</name>\n"
+            "        <transport>hwbinder</transport>\n"
+            "        <version>1.0</version>\n"
+            "        <interface>\n"
+            "            <name>IFoo</name>\n"
+            "            <instance>default</instance>\n"
+            "            <instance>legacy/0</instance>\n"
+            "            <instance>legacy/1</instance>\n"
+            "        </interface>\n"
+            "    </hal>\n"
+            "</manifest>\n";
+
+        HalManifest manifest;
+        EXPECT_TRUE(gHalManifestConverter(&manifest, xml));
+        EXPECT_TRUE(manifest.checkCompatibility(matrix, &error)) << error;
+
+        auto unused = checkUnusedHals(manifest, matrix);
+        EXPECT_TRUE(unused.empty())
+            << "Contains unused HALs: " << android::base::Join(unused, "\n");
+    }
+
+    {
+        std::string xml =
+            "<manifest version=\"1.0\" type=\"device\">\n"
+            "    <hal format=\"hidl\">\n"
+            "        <name>android.hardware.foo</name>\n"
+            "        <transport>hwbinder</transport>\n"
+            "        <version>1.0</version>\n"
+            "        <interface>\n"
+            "            <name>IFoo</name>\n"
+            "            <instance>default</instance>\n"
+            "            <instance>legacy0</instance>\n"
+            "            <instance>nonmatch/legacy/0</instance>\n"
+            "            <instance>legacy/0/nonmatch</instance>\n"
+            "        </interface>\n"
+            "    </hal>\n"
+            "</manifest>\n";
+
+        HalManifest manifest;
+        EXPECT_TRUE(gHalManifestConverter(&manifest, xml));
+        EXPECT_FALSE(manifest.checkCompatibility(matrix, &error))
+            << "Should not be compatible because no legacy/[0-9]+ is provided.";
+
+        auto unused = checkUnusedHals(manifest, matrix);
+        EXPECT_EQ((std::set<std::string>{"android.hardware.foo@1.0::IFoo/nonmatch/legacy/0",
+                                         "android.hardware.foo@1.0::IFoo/legacy/0/nonmatch",
+                                         "android.hardware.foo@1.0::IFoo/legacy0"}),
+                  unused);
+    }
+}
+
+TEST_F(LibVintfTest, Regex) {
+    details::Regex regex;
+
+    EXPECT_FALSE(regex.compile("+"));
+    EXPECT_FALSE(regex.compile("*"));
+
+    ASSERT_TRUE(regex.compile("legacy/[0-9]+"));
+    EXPECT_TRUE(regex.matches("legacy/0"));
+    EXPECT_TRUE(regex.matches("legacy/000"));
+    EXPECT_FALSE(regex.matches("legacy/"));
+    EXPECT_FALSE(regex.matches("ssslegacy/0"));
+    EXPECT_FALSE(regex.matches("legacy/0sss"));
+}
+
 } // namespace vintf
 } // namespace android
 
diff --git a/test/vintf_object_tests.cpp b/test/vintf_object_tests.cpp
index 27c04a5..48623b8 100644
--- a/test/vintf_object_tests.cpp
+++ b/test/vintf_object_tests.cpp
@@ -227,6 +227,54 @@
     "    </hal>\n"
     "</compatibility-matrix>\n";
 
+//
+// Set of framework matrices of different FCM version with regex.
+//
+
+const static std::vector<std::string> systemMatrixRegexXmls = {
+    // 1.xml
+    "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"1\">\n"
+    "    <hal format=\"hidl\" optional=\"false\">\n"
+    "        <name>android.hardware.regex</name>\n"
+    "        <version>1.0-1</version>\n"
+    "        <interface>\n"
+    "            <name>IRegex</name>\n"
+    "            <instance>default</instance>\n"
+    "            <instance>special/1.0</instance>\n"
+    "            <regex-instance>regex/1.0/[0-9]+</regex-instance>\n"
+    "            <regex-instance>regex_common/[0-9]+</regex-instance>\n"
+    "        </interface>\n"
+    "    </hal>\n"
+    "</compatibility-matrix>\n",
+    // 2.xml
+    "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"2\">\n"
+    "    <hal format=\"hidl\" optional=\"false\">\n"
+    "        <name>android.hardware.regex</name>\n"
+    "        <version>1.1-2</version>\n"
+    "        <interface>\n"
+    "            <name>IRegex</name>\n"
+    "            <instance>default</instance>\n"
+    "            <instance>special/1.1</instance>\n"
+    "            <regex-instance>regex/1.1/[0-9]+</regex-instance>\n"
+    "            <regex-instance>[a-z]+_[a-z]+/[0-9]+</regex-instance>\n"
+    "        </interface>\n"
+    "    </hal>\n"
+    "</compatibility-matrix>\n",
+    // 3.xml
+    "<compatibility-matrix version=\"1.0\" type=\"framework\" level=\"3\">\n"
+    "    <hal format=\"hidl\" optional=\"false\">\n"
+    "        <name>android.hardware.regex</name>\n"
+    "        <version>2.0</version>\n"
+    "        <interface>\n"
+    "            <name>IRegex</name>\n"
+    "            <instance>default</instance>\n"
+    "            <instance>special/2.0</instance>\n"
+    "            <regex-instance>regex/2.0/[0-9]+</regex-instance>\n"
+    "            <regex-instance>regex_[a-z]+/[0-9]+</regex-instance>\n"
+    "        </interface>\n"
+    "    </hal>\n"
+    "</compatibility-matrix>\n"};
+
 // Setup the MockFileFetcher used by the fetchAllInformation template
 // so it returns the given metadata info instead of fetching from device.
 void setupMockFetcher(const std::string& vendorManifestXml, const std::string& systemMatrixXml,
@@ -332,6 +380,16 @@
             }));
     }
 
+    // Expect that a file exist and can be fetched 0 or more times.
+    void expectFetchRepeatedly(const std::string& path, const std::string& content) {
+        EXPECT_CALL(fetcher(), fetch(StrEq(path), _))
+            .Times(AnyNumber())
+            .WillRepeatedly(Invoke([content](const auto&, auto& out) {
+                out = content;
+                return ::android::OK;
+            }));
+    }
+
     // Expect that the file should never be fetched (whether it exists or not).
     void expectNeverFetch(const std::string& path) {
         EXPECT_CALL(fetcher(), fetch(StrEq(path), _)).Times(0);
@@ -959,6 +1017,201 @@
         << "major@1.0 should be deprecated. " << error;
 }
 
+class RegexTest : public VintfObjectTestBase {
+   protected:
+    static std::string getFileName(size_t i) {
+        return "compatibility_matrix." + std::to_string(static_cast<Level>(i)) + ".xml";
+    }
+    virtual void SetUp() override {
+        EXPECT_CALL(fetcher(), listFiles(StrEq(kSystemVintfDir), _, _))
+            .WillRepeatedly(Invoke([](const auto&, auto* out, auto*) {
+                size_t i = 1;
+                for (const auto& content : systemMatrixRegexXmls) {
+                    (void)content;
+                    out->push_back(getFileName(i));
+                    ++i;
+                }
+                return ::android::OK;
+            }));
+        size_t i = 1;
+        for (const auto& content : systemMatrixRegexXmls) {
+            expectFetchRepeatedly(kSystemVintfDir + getFileName(i), content);
+            ++i;
+        }
+        expectSystemMatrix(0);
+        expectFileNotExist(StartsWith("/odm/"));
+    }
+    void expectTargetFcmVersion(size_t level) {
+        expectFetch(kVendorManifest, "<manifest version=\"1.0\" type=\"device\" target-level=\"" +
+                                         to_string(static_cast<Level>(level)) + "\"/>");
+        VintfObject::GetDeviceHalManifest(true /* skipCache */);
+    }
+};
+
+TEST_F(RegexTest, CombineLevel1) {
+    expectTargetFcmVersion(1);
+    auto matrix = VintfObject::GetFrameworkCompatibilityMatrix(true /* skipCache */);
+    ASSERT_NE(nullptr, matrix);
+    std::string xml = gCompatibilityMatrixConverter(*matrix);
+
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>1.0-2</version>\n"
+        "        <version>2.0</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>default</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>1.0-1</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>special/1.0</instance>\n"
+        "            <regex-instance>regex/1.0/[0-9]+</regex-instance>\n"
+        "            <regex-instance>regex_common/[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"true\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>1.1-2</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>special/1.1</instance>\n"
+        "            <regex-instance>[a-z]+_[a-z]+/[0-9]+</regex-instance>\n"
+        "            <regex-instance>regex/1.1/[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"true\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>2.0</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>special/2.0</instance>\n"
+        "            <regex-instance>regex/2.0/[0-9]+</regex-instance>\n"
+        "            <regex-instance>regex_[a-z]+/[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+}
+
+TEST_F(RegexTest, CombineLevel2) {
+    expectTargetFcmVersion(2);
+    auto matrix = VintfObject::GetFrameworkCompatibilityMatrix(true /* skipCache */);
+    ASSERT_NE(nullptr, matrix);
+    std::string xml = gCompatibilityMatrixConverter(*matrix);
+
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>1.1-2</version>\n"
+        "        <version>2.0</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>default</instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"false\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>1.1-2</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>special/1.1</instance>\n"
+        "            <regex-instance>[a-z]+_[a-z]+/[0-9]+</regex-instance>\n"
+        "            <regex-instance>regex/1.1/[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+    EXPECT_IN(
+        "    <hal format=\"hidl\" optional=\"true\">\n"
+        "        <name>android.hardware.regex</name>\n"
+        "        <version>2.0</version>\n"
+        "        <interface>\n"
+        "            <name>IRegex</name>\n"
+        "            <instance>special/2.0</instance>\n"
+        "            <regex-instance>regex/2.0/[0-9]+</regex-instance>\n"
+        "            <regex-instance>regex_[a-z]+/[0-9]+</regex-instance>\n"
+        "        </interface>\n"
+        "    </hal>\n",
+        xml);
+}
+
+TEST_F(RegexTest, DeprecateLevel2) {
+    std::string error;
+    expectTargetFcmVersion(2);
+
+    auto pred = getInstanceListFunc({
+        "android.hardware.regex@1.1::IRegex/default",
+        "android.hardware.regex@1.1::IRegex/special/1.1",
+        "android.hardware.regex@1.1::IRegex/regex/1.1/1",
+        "android.hardware.regex@1.1::IRegex/regex_common/0",
+        "android.hardware.regex@2.0::IRegex/default",
+    });
+    EXPECT_EQ(NO_DEPRECATED_HALS, VintfObject::CheckDeprecation(pred, &error)) << error;
+
+    for (const auto& deprecated : {
+             "android.hardware.regex@1.0::IRegex/default",
+             "android.hardware.regex@1.0::IRegex/special/1.0",
+             "android.hardware.regex@1.0::IRegex/regex/1.0/1",
+             "android.hardware.regex@1.0::IRegex/regex_common/0",
+             "android.hardware.regex@1.1::IRegex/special/1.0",
+             "android.hardware.regex@1.1::IRegex/regex/1.0/1",
+         }) {
+        // 2.0/default ensures compatibility.
+        pred = getInstanceListFunc({
+            deprecated,
+            "android.hardware.regex@2.0::IRegex/default",
+        });
+        error.clear();
+        EXPECT_EQ(DEPRECATED, VintfObject::CheckDeprecation(pred, &error))
+            << deprecated << " should be deprecated. " << error;
+    }
+}
+
+TEST_F(RegexTest, DeprecateLevel3) {
+    std::string error;
+    expectTargetFcmVersion(3);
+
+    auto pred = getInstanceListFunc({
+        "android.hardware.regex@2.0::IRegex/special/2.0",
+        "android.hardware.regex@2.0::IRegex/regex/2.0/1",
+        "android.hardware.regex@2.0::IRegex/default",
+    });
+    EXPECT_EQ(NO_DEPRECATED_HALS, VintfObject::CheckDeprecation(pred, &error)) << error;
+
+    for (const auto& deprecated : {
+             "android.hardware.regex@1.0::IRegex/default",
+             "android.hardware.regex@1.0::IRegex/special/1.0",
+             "android.hardware.regex@1.0::IRegex/regex/1.0/1",
+             "android.hardware.regex@1.0::IRegex/regex_common/0",
+             "android.hardware.regex@1.1::IRegex/special/1.0",
+             "android.hardware.regex@1.1::IRegex/regex/1.0/1",
+             "android.hardware.regex@1.1::IRegex/special/1.1",
+             "android.hardware.regex@1.1::IRegex/regex/1.1/1",
+             "android.hardware.regex@1.1::IRegex/regex_common/0",
+         }) {
+        // 2.0/default ensures compatibility.
+        pred = getInstanceListFunc({
+            deprecated,
+            "android.hardware.regex@2.0::IRegex/default",
+        });
+
+        error.clear();
+        EXPECT_EQ(DEPRECATED, VintfObject::CheckDeprecation(pred, &error))
+            << deprecated << " should be deprecated.";
+    }
+}
+
 int main(int argc, char** argv) {
     ::testing::InitGoogleMock(&argc, argv);