diff --git a/audio/2.0/vts/functional/Android.mk b/audio/2.0/vts/functional/Android.mk index 8af18bea0e..db08dde117 100644 --- a/audio/2.0/vts/functional/Android.mk +++ b/audio/2.0/vts/functional/Android.mk @@ -21,6 +21,8 @@ LOCAL_MODULE := VtsHalAudioV2_0TargetTest LOCAL_CPPFLAGS := -O0 -g -Wall -Wextra -Werror LOCAL_SRC_FILES := \ AudioPrimaryHidlHalTest.cpp \ + ValidateAudioConfiguration.cpp \ + utility/ValidateXml.cpp LOCAL_C_INCLUDES := external/libxml2/include diff --git a/audio/2.0/vts/functional/ValidateAudioConfiguration.cpp b/audio/2.0/vts/functional/ValidateAudioConfiguration.cpp new file mode 100644 index 0000000000..01324c87d3 --- /dev/null +++ b/audio/2.0/vts/functional/ValidateAudioConfiguration.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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 "utility/ValidateXml.h" + +TEST(CheckConfig, audioPolicyConfigurationValidation) { + ASSERT_VALID_XML("/vendor/etc/audio_policy_configuration.xml", + "/data/local/tmp/audio_policy_configuration.xsd"); +} diff --git a/audio/2.0/vts/functional/utility/ValidateXml.cpp b/audio/2.0/vts/functional/utility/ValidateXml.cpp new file mode 100644 index 0000000000..ff08d2dc5a --- /dev/null +++ b/audio/2.0/vts/functional/utility/ValidateXml.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2017 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. + */ + +#define LOG_TAG "ValidateAudioConfig" +#include + +#define LIBXML_SCHEMAS_ENABLED +#include +#define LIBXML_XINCLUDE_ENABLED +#include + +#include +#include + +#include "ValidateXml.h" + +namespace android { +namespace hardware { +namespace audio { +namespace test { + +/** Map libxml2 structures to their corresponding deleters. */ +template +constexpr void (*xmlDeleter)(T* t); +template <> +constexpr auto xmlDeleter = xmlSchemaFree; +template <> +constexpr auto xmlDeleter = xmlFreeDoc; +template <> +constexpr auto xmlDeleter = xmlSchemaFreeParserCtxt; +template <> +constexpr auto xmlDeleter = xmlSchemaFreeValidCtxt; + +/** @return a unique_ptr with the correct deleter for the libxml2 object. */ +template +constexpr auto make_xmlUnique(T* t) { + // Wrap deleter in lambda to enable empty base optimization + auto deleter = [](T* t) { xmlDeleter(t); }; + return std::unique_ptr{t, deleter}; +} + +/** Class that handles libxml2 initialization and cleanup. NOT THREAD SAFE*/ +struct Libxml2Global { + Libxml2Global() { + xmlLineNumbersDefault(1); // Better error message + xmlSetGenericErrorFunc(this, errorCb); + } + ~Libxml2Global() { + // TODO: check if all those cleanup are needed + xmlSetGenericErrorFunc(nullptr, nullptr); + xmlSchemaCleanupTypes(); + xmlCleanupParser(); + xmlCleanupThreads(); + } + + const std::string& getErrors() { return errors; } + + private: + static void errorCb(void* ctxt, const char* msg, ...) { + auto* self = static_cast(ctxt); + va_list args; + va_start(args, msg); + + char* formatedMsg; + if (vasprintf(&formatedMsg, msg, args) >= 0) { + LOG_PRI(ANDROID_LOG_ERROR, LOG_TAG, "%s", formatedMsg); + self->errors += "Error: "; + self->errors += formatedMsg; + } + free(formatedMsg); + + va_end(args); + } + std::string errors; +}; + +::testing::AssertionResult validateXml(const char* xmlFilePathExpr, + const char* xsdFilePathExpr, + const char* xmlFilePath, + const char* xsdFilePath) { + Libxml2Global libxml2; + + auto context = [&]() { + return std::string() + " While validating: " + xmlFilePathExpr + + "\n Which is: " + xmlFilePath + + "\nAgainst the schema: " + xsdFilePathExpr + + "\n Which is: " + xsdFilePath + "Libxml2 errors\n" + + libxml2.getErrors(); + }; + + auto schemaParserCtxt = make_xmlUnique(xmlSchemaNewParserCtxt(xsdFilePath)); + auto schema = make_xmlUnique(xmlSchemaParse(schemaParserCtxt.get())); + if (schema == nullptr) { + return ::testing::AssertionFailure() << "Failed to parse schema (xsd)\n" + << context(); + } + + auto doc = make_xmlUnique(xmlReadFile(xmlFilePath, nullptr, 0)); + if (doc == nullptr) { + return ::testing::AssertionFailure() << "Failed to parse xml\n" + << context(); + } + + if (xmlXIncludeProcess(doc.get()) == -1) { + return ::testing::AssertionFailure() + << "Failed to resolve xincludes in xml\n" + << context(); + } + + auto schemaCtxt = make_xmlUnique(xmlSchemaNewValidCtxt(schema.get())); + int ret = xmlSchemaValidateDoc(schemaCtxt.get(), doc.get()); + if (ret > 0) { + return ::testing::AssertionFailure() + << "xml is not valid according to the xsd.\n" + << context(); + } + if (ret < 0) { + return ::testing::AssertionFailure() << "Internal or API error\n" + << context(); + } + + return ::testing::AssertionSuccess(); +} + +} // namespace test +} // namespace audio +} // namespace hardware +} // namespace android diff --git a/audio/2.0/vts/functional/utility/ValidateXml.h b/audio/2.0/vts/functional/utility/ValidateXml.h new file mode 100644 index 0000000000..619dd4061b --- /dev/null +++ b/audio/2.0/vts/functional/utility/ValidateXml.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef ANDROID_HARDWARE_AUDIO_TEST_VALIDATEXML +#define ANDROID_HARDWARE_AUDIO_TEST_VALIDATEXML + +#include + +namespace android { +namespace hardware { +namespace audio { +namespace test { + +/** Validate the provided XmlFile with the provided xsdFile. + * Intended to use with ASSERT_PRED_FORMAT2 as such: + * ASSERT_PRED_FORMAT2(validateXml, pathToXml, pathToXsd); + * See ASSERT_VALID_XML for a helper macro. + */ +::testing::AssertionResult validateXml(const char* xmlFilePathExpr, + const char* xsdFilePathExpr, + const char* xmlFilePath, + const char* xsdPathName); + +/** Helper gtest ASSERT to test xml validity against an xsd. */ +#define ASSERT_VALID_XML(xmlFilePath, xsdFilePath) \ + ASSERT_PRED_FORMAT2(::android::hardware::audio::test::validateXml, \ + xmlFilePath, xsdFilePath) + +} // namespace test +} // namespace audio +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_AUDIO_TEST_VALIDATEXML