119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard/*
219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * Copyright (C) 2017 The Android Open Source Project
319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard *
419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * Licensed under the Apache License, Version 2.0 (the "License");
519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * you may not use this file except in compliance with the License.
619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * You may obtain a copy of the License at
719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard *
819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard *      http://www.apache.org/licenses/LICENSE-2.0
919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard *
1019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * Unless required by applicable law or agreed to in writing, software
1119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * distributed under the License is distributed on an "AS IS" BASIS,
1219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * See the License for the specific language governing permissions and
1419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard * limitations under the License.
1519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard */
1619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
1719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#define LOG_TAG "ValidateAudioConfig"
1819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#include <utils/Log.h>
1919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
2019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#define LIBXML_SCHEMAS_ENABLED
2119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#include <libxml/xmlschemastypes.h>
2219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#define LIBXML_XINCLUDE_ENABLED
2319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#include <libxml/xinclude.h>
2419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
2519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#include <memory>
2619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#include <string>
2719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
2819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard#include "ValidateXml.h"
2919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
3019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardnamespace android {
3119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardnamespace hardware {
3219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardnamespace audio {
3319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardnamespace test {
3419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
3519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard/** Map libxml2 structures to their corresponding deleters. */
3619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardtemplate <class T>
3719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardconstexpr void (*xmlDeleter)(T* t);
3819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardtemplate <>
3919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardconstexpr auto xmlDeleter<xmlSchema> = xmlSchemaFree;
4019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardtemplate <>
4119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardconstexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
4219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardtemplate <>
4319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardconstexpr auto xmlDeleter<xmlSchemaParserCtxt> = xmlSchemaFreeParserCtxt;
4419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardtemplate <>
4519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardconstexpr auto xmlDeleter<xmlSchemaValidCtxt> = xmlSchemaFreeValidCtxt;
4619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
4719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard/** @return a unique_ptr with the correct deleter for the libxml2 object. */
4819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardtemplate <class T>
4919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardconstexpr auto make_xmlUnique(T* t) {
5019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    // Wrap deleter in lambda to enable empty base optimization
5119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    auto deleter = [](T* t) { xmlDeleter<T>(t); };
5219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    return std::unique_ptr<T, decltype(deleter)>{t, deleter};
5319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard}
5419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
5519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard/** Class that handles libxml2 initialization and cleanup. NOT THREAD SAFE*/
5619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocardstruct Libxml2Global {
5719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    Libxml2Global() {
5819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        xmlLineNumbersDefault(1);  // Better error message
5919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        xmlSetGenericErrorFunc(this, errorCb);
6019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
6119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    ~Libxml2Global() {
6219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        // TODO: check if all those cleanup are needed
6319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        xmlSetGenericErrorFunc(nullptr, nullptr);
6419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        xmlSchemaCleanupTypes();
6519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        xmlCleanupParser();
6619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        xmlCleanupThreads();
6719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
6819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
6919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    const std::string& getErrors() { return errors; }
7019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
7119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard   private:
7219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    static void errorCb(void* ctxt, const char* msg, ...) {
7319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        auto* self = static_cast<Libxml2Global*>(ctxt);
7419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        va_list args;
7519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        va_start(args, msg);
7619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
7719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        char* formatedMsg;
7819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        if (vasprintf(&formatedMsg, msg, args) >= 0) {
7919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard            LOG_PRI(ANDROID_LOG_ERROR, LOG_TAG, "%s", formatedMsg);
8019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard            self->errors += "Error: ";
8119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard            self->errors += formatedMsg;
8219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        }
8319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        free(formatedMsg);
8419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
8519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        va_end(args);
8619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
8719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    std::string errors;
8819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard};
8919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
9019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard::testing::AssertionResult validateXml(const char* xmlFilePathExpr,
9119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard                                       const char* xsdFilePathExpr,
9219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard                                       const char* xmlFilePath,
9319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard                                       const char* xsdFilePath) {
9419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    Libxml2Global libxml2;
9519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
9619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    auto context = [&]() {
9719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        return std::string() + "    While validating: " + xmlFilePathExpr +
9819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               "\n          Which is: " + xmlFilePath +
9919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               "\nAgainst the schema: " + xsdFilePathExpr +
10019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               "\n          Which is: " + xsdFilePath + "Libxml2 errors\n" +
10119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               libxml2.getErrors();
10219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    };
10319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
10419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    auto schemaParserCtxt = make_xmlUnique(xmlSchemaNewParserCtxt(xsdFilePath));
10519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    auto schema = make_xmlUnique(xmlSchemaParse(schemaParserCtxt.get()));
10619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    if (schema == nullptr) {
10719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        return ::testing::AssertionFailure() << "Failed to parse schema (xsd)\n"
10819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard                                             << context();
10919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
11019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
11119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    auto doc = make_xmlUnique(xmlReadFile(xmlFilePath, nullptr, 0));
11219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    if (doc == nullptr) {
11319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        return ::testing::AssertionFailure() << "Failed to parse xml\n"
11419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard                                             << context();
11519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
11619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
11719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    if (xmlXIncludeProcess(doc.get()) == -1) {
11819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        return ::testing::AssertionFailure()
11919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               << "Failed to resolve xincludes in xml\n"
12019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               << context();
12119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
12219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
12319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    auto schemaCtxt = make_xmlUnique(xmlSchemaNewValidCtxt(schema.get()));
12419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    int ret = xmlSchemaValidateDoc(schemaCtxt.get(), doc.get());
12519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    if (ret > 0) {
12619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        return ::testing::AssertionFailure()
12719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               << "xml is not valid according to the xsd.\n"
12819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard               << context();
12919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
13019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    if (ret < 0) {
13119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard        return ::testing::AssertionFailure() << "Internal or API error\n"
13219b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard                                             << context();
13319b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    }
13419b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
13519b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard    return ::testing::AssertionSuccess();
13619b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard}
13719b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard
13819b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard}  // namespace test
13919b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard}  // namespace audio
14019b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard}  // namespace hardware
14119b3e43fb4e639985fde0219bb1bb491abf88537Kevin Rocard}  // namespace android
142