Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "ValidateAudioConfig"
     18 #include <utils/Log.h>
     19 
     20 #define LIBXML_SCHEMAS_ENABLED
     21 #include <libxml/xmlschemastypes.h>
     22 #define LIBXML_XINCLUDE_ENABLED
     23 #include <libxml/xinclude.h>
     24 
     25 #include <memory>
     26 #include <string>
     27 
     28 #include "ValidateXml.h"
     29 
     30 namespace android {
     31 namespace hardware {
     32 namespace audio {
     33 namespace common {
     34 namespace test {
     35 namespace utility {
     36 
     37 /** Map libxml2 structures to their corresponding deleters. */
     38 template <class T>
     39 constexpr void (*xmlDeleter)(T* t);
     40 template <>
     41 constexpr auto xmlDeleter<xmlSchema> = xmlSchemaFree;
     42 template <>
     43 constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
     44 template <>
     45 constexpr auto xmlDeleter<xmlSchemaParserCtxt> = xmlSchemaFreeParserCtxt;
     46 template <>
     47 constexpr auto xmlDeleter<xmlSchemaValidCtxt> = xmlSchemaFreeValidCtxt;
     48 
     49 /** @return a unique_ptr with the correct deleter for the libxml2 object. */
     50 template <class T>
     51 constexpr auto make_xmlUnique(T* t) {
     52     // Wrap deleter in lambda to enable empty base optimization
     53     auto deleter = [](T* t) { xmlDeleter<T>(t); };
     54     return std::unique_ptr<T, decltype(deleter)>{t, deleter};
     55 }
     56 
     57 /** Class that handles libxml2 initialization and cleanup. NOT THREAD SAFE*/
     58 struct Libxml2Global {
     59     Libxml2Global() {
     60         xmlLineNumbersDefault(1);  // Better error message
     61         xmlSetGenericErrorFunc(this, errorCb);
     62     }
     63     ~Libxml2Global() {
     64         // TODO: check if all those cleanup are needed
     65         xmlSetGenericErrorFunc(nullptr, nullptr);
     66         xmlSchemaCleanupTypes();
     67         xmlCleanupParser();
     68         xmlCleanupThreads();
     69     }
     70 
     71     const std::string& getErrors() { return errors; }
     72 
     73    private:
     74     static void errorCb(void* ctxt, const char* msg, ...) {
     75         auto* self = static_cast<Libxml2Global*>(ctxt);
     76         va_list args;
     77         va_start(args, msg);
     78 
     79         char* formatedMsg;
     80         if (vasprintf(&formatedMsg, msg, args) >= 0) {
     81             LOG_PRI(ANDROID_LOG_ERROR, LOG_TAG, "%s", formatedMsg);
     82             self->errors += "Error: ";
     83             self->errors += formatedMsg;
     84         }
     85         free(formatedMsg);
     86 
     87         va_end(args);
     88     }
     89     std::string errors;
     90 };
     91 
     92 ::testing::AssertionResult validateXml(const char* xmlFilePathExpr, const char* xsdFilePathExpr,
     93                                        const char* xmlFilePath, const char* xsdFilePath) {
     94     Libxml2Global libxml2;
     95 
     96     auto context = [&]() {
     97         return std::string() + "    While validating: " + xmlFilePathExpr +
     98                "\n          Which is: " + xmlFilePath + "\nAgainst the schema: " + xsdFilePathExpr +
     99                "\n          Which is: " + xsdFilePath + "Libxml2 errors\n" + libxml2.getErrors();
    100     };
    101 
    102     auto schemaParserCtxt = make_xmlUnique(xmlSchemaNewParserCtxt(xsdFilePath));
    103     auto schema = make_xmlUnique(xmlSchemaParse(schemaParserCtxt.get()));
    104     if (schema == nullptr) {
    105         return ::testing::AssertionFailure() << "Failed to parse schema (xsd)\n" << context();
    106     }
    107 
    108     auto doc = make_xmlUnique(xmlReadFile(xmlFilePath, nullptr, 0));
    109     if (doc == nullptr) {
    110         return ::testing::AssertionFailure() << "Failed to parse xml\n" << context();
    111     }
    112 
    113     if (xmlXIncludeProcess(doc.get()) == -1) {
    114         return ::testing::AssertionFailure() << "Failed to resolve xincludes in xml\n" << context();
    115     }
    116 
    117     auto schemaCtxt = make_xmlUnique(xmlSchemaNewValidCtxt(schema.get()));
    118     int ret = xmlSchemaValidateDoc(schemaCtxt.get(), doc.get());
    119     if (ret > 0) {
    120         return ::testing::AssertionFailure() << "xml is not valid according to the xsd.\n"
    121                                              << context();
    122     }
    123     if (ret < 0) {
    124         return ::testing::AssertionFailure() << "Internal or API error\n" << context();
    125     }
    126 
    127     return ::testing::AssertionSuccess();
    128 }
    129 
    130 }  // utility
    131 }  // test
    132 }  // common
    133 }  // audio
    134 }  // test
    135 }  // utility
    136