Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 068de276 authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

audio: Fix handling of relative XML include paths in VTS

Pass "no fixup base dirs" flag to the XInclude processor
to avoid modifications of the top-level elements from
included XML files as a result of "fixup."

Added tests to ensure that all relevant XInclude scenarios
work.

Bug: 192619060
Test: atest -host android.hardware.audio.common.test.utility_tests
Change-Id: Id595c9fd30be378d76387ee55a8937e0bf28d1cd
(cherry picked from commit 13c67965)
Merged-In: Id595c9fd30be378d76387ee55a8937e0bf28d1cd
parent 9f834f1a
Loading
Loading
Loading
Loading
+29 −2
Original line number Diff line number Diff line
@@ -34,7 +34,34 @@ cc_library_static {
    ],
    local_include_dirs: ["include/utility"],
    export_include_dirs: ["include"],
    shared_libs: ["libxml2", "liblog"],
    shared_libs: [
        "libxml2",
        "liblog",
    ],
    static_libs: ["libgtest"],
    export_static_lib_headers: ["libgtest"],
}

// Note: this isn't a VTS test, but rather a unit test
// to verify correctness of test utilities.
cc_test {
    name: "android.hardware.audio.common.test.utility_tests",
    host_supported: true,
    local_include_dirs: ["include/utility"],
    srcs: [
        "src/ValidateXml.cpp",
        "tests/utility_tests.cpp",
    ],
    cflags: [
        "-Werror",
        "-Wall",
        "-g",
    ],
    shared_libs: [
        "libbase",
        "libxml2",
        "liblog",
    ],
    static_libs: ["libgtest"],
    test_suites: ["general-tests"],
}
+7 −0
Original line number Diff line number Diff line
{
  "presubmit": [
    {
      "name": "android.hardware.audio.common.test.utility_tests"
    }
  ]
}
+2 −1
Original line number Diff line number Diff line
@@ -112,7 +112,8 @@ struct Libxml2Global {
        return ::testing::AssertionFailure() << "Failed to parse xml\n" << context();
    }

    if (xmlXIncludeProcess(doc.get()) == -1) {
    // Process 'include' directives w/o modifying elements loaded from included files.
    if (xmlXIncludeProcessFlags(doc.get(), XML_PARSE_NOBASEFIX) == -1) {
        return ::testing::AssertionFailure() << "Failed to resolve xincludes in xml\n" << context();
    }

+176 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <android-base/file.h>
#include <gtest/gtest.h>

#include <ValidateXml.h>

using ::android::hardware::audio::common::test::utility::validateXml;

const char* XSD_SOURCE =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        "<xs:schema version=\"2.0\""
        "           elementFormDefault=\"qualified\""
        "           attributeFormDefault=\"unqualified\""
        "           xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
        "  <xs:element name=\"audioPolicyConfiguration\">"
        "    <xs:complexType>"
        "      <xs:sequence>"
        "        <xs:element name=\"modules\">"
        "          <xs:complexType>"
        "            <xs:sequence>"
        "              <xs:element name=\"module\" maxOccurs=\"unbounded\">"
        "                <xs:complexType>"
        "                  <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>"
        "                </xs:complexType>"
        "              </xs:element>"
        "            </xs:sequence>"
        "          </xs:complexType>"
        "        </xs:element>"
        "      </xs:sequence>"
        "    </xs:complexType>"
        "  </xs:element>"
        "</xs:schema>";

const char* INVALID_XML_SOURCE =
        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
        "<audioPolicyKonfiguration />";

const char* VALID_XML_SOURCE =
        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
        "<audioPolicyConfiguration>"
        "  <modules>"
        "    <module name=\"aaa\" />"
        "    %s"
        "  </modules>"
        "</audioPolicyConfiguration>";

const char* MODULE_SOURCE = "<module name=\"bbb\" />";

const char* XI_INCLUDE = "<xi:include xmlns:xi=\"http://www.w3.org/2001/XInclude\" href=\"%s\" />";

const char* XML_INCLUDED_SOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%s";

namespace {

std::string substitute(const char* fmt, const char* param) {
    std::string buffer(static_cast<size_t>(strlen(fmt) + strlen(param)), '\0');
    snprintf(buffer.data(), buffer.size(), fmt, param);
    return buffer;
}

std::string substitute(const char* fmt, const std::string& s) {
    return substitute(fmt, s.c_str());
}

}  // namespace

TEST(ValidateXml, InvalidXml) {
    TemporaryFile xml;
    ASSERT_TRUE(android::base::WriteStringToFile(INVALID_XML_SOURCE, xml.path)) << strerror(errno);
    TemporaryFile xsd;
    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
    EXPECT_FALSE(validateXml("xml", "xsd", xml.path, xsd.path));
}

TEST(ValidateXml, ValidXml) {
    TemporaryFile xml;
    ASSERT_TRUE(
            android::base::WriteStringToFile(substitute(VALID_XML_SOURCE, MODULE_SOURCE), xml.path))
            << strerror(errno);
    TemporaryFile xsd;
    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
}

TEST(ValidateXml, IncludeAbsolutePath) {
    TemporaryFile xmlInclude;
    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
                                                 xmlInclude.path))
            << strerror(errno);
    TemporaryFile xml;
    ASSERT_TRUE(android::base::WriteStringToFile(
            substitute(VALID_XML_SOURCE, substitute(XI_INCLUDE, xmlInclude.path)), xml.path))
            << strerror(errno);
    TemporaryFile xsd;
    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
}

TEST(ValidateXml, IncludeSameDirRelativePath) {
    TemporaryFile xmlInclude;
    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
                                                 xmlInclude.path))
            << strerror(errno);
    TemporaryFile xml;
    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlInclude.path));
    ASSERT_TRUE(android::base::WriteStringToFile(
            substitute(VALID_XML_SOURCE,
                       substitute(XI_INCLUDE, android::base::Basename(xmlInclude.path))),
            xml.path))
            << strerror(errno);
    TemporaryFile xsd;
    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
}

TEST(ValidateXml, IncludeSubdirRelativePath) {
    TemporaryDir xmlIncludeDir;
    TemporaryFile xmlInclude(xmlIncludeDir.path);
    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
                                                 xmlInclude.path))
            << strerror(errno);
    TemporaryFile xml;
    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlIncludeDir.path));
    ASSERT_TRUE(android::base::WriteStringToFile(
            substitute(VALID_XML_SOURCE,
                       substitute(XI_INCLUDE, android::base::Basename(xmlIncludeDir.path) + "/" +
                                                      android::base::Basename(xmlInclude.path))),
            xml.path))
            << strerror(errno);
    TemporaryFile xsd;
    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
}

TEST(ValidateXml, IncludeParentDirRelativePath) {
    // An XML file from a subdirectory includes a file from the parent directory using '..' syntax.
    TemporaryFile xmlInclude;
    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
                                                 xmlInclude.path))
            << strerror(errno);
    TemporaryDir xmlIncludeDir;
    TemporaryFile xmlParentInclude(xmlIncludeDir.path);
    ASSERT_TRUE(android::base::WriteStringToFile(
            substitute(XML_INCLUDED_SOURCE,
                       substitute(XI_INCLUDE, "../" + android::base::Basename(xmlInclude.path))),
            xmlParentInclude.path))
            << strerror(errno);
    TemporaryFile xml;
    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlInclude.path));
    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlIncludeDir.path));
    ASSERT_TRUE(android::base::WriteStringToFile(
            substitute(
                    VALID_XML_SOURCE,
                    substitute(XI_INCLUDE, android::base::Basename(xmlIncludeDir.path) + "/" +
                                                   android::base::Basename(xmlParentInclude.path))),
            xml.path))
            << strerror(errno);
    TemporaryFile xsd;
    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
}