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 test { 34 35 /** Map libxml2 structures to their corresponding deleters. */ 36 template <class T> 37 constexpr void (*xmlDeleter)(T* t); 38 template <> 39 constexpr auto xmlDeleter<xmlSchema> = xmlSchemaFree; 40 template <> 41 constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc; 42 template <> 43 constexpr auto xmlDeleter<xmlSchemaParserCtxt> = xmlSchemaFreeParserCtxt; 44 template <> 45 constexpr auto xmlDeleter<xmlSchemaValidCtxt> = xmlSchemaFreeValidCtxt; 46 47 /** @return a unique_ptr with the correct deleter for the libxml2 object. */ 48 template <class T> 49 constexpr auto make_xmlUnique(T* t) { 50 // Wrap deleter in lambda to enable empty base optimization 51 auto deleter = [](T* t) { xmlDeleter<T>(t); }; 52 return std::unique_ptr<T, decltype(deleter)>{t, deleter}; 53 } 54 55 /** Class that handles libxml2 initialization and cleanup. NOT THREAD SAFE*/ 56 struct Libxml2Global { 57 Libxml2Global() { 58 xmlLineNumbersDefault(1); // Better error message 59 xmlSetGenericErrorFunc(this, errorCb); 60 } 61 ~Libxml2Global() { 62 // TODO: check if all those cleanup are needed 63 xmlSetGenericErrorFunc(nullptr, nullptr); 64 xmlSchemaCleanupTypes(); 65 xmlCleanupParser(); 66 xmlCleanupThreads(); 67 } 68 69 const std::string& getErrors() { return errors; } 70 71 private: 72 static void errorCb(void* ctxt, const char* msg, ...) { 73 auto* self = static_cast<Libxml2Global*>(ctxt); 74 va_list args; 75 va_start(args, msg); 76 77 char* formatedMsg; 78 if (vasprintf(&formatedMsg, msg, args) >= 0) { 79 LOG_PRI(ANDROID_LOG_ERROR, LOG_TAG, "%s", formatedMsg); 80 self->errors += "Error: "; 81 self->errors += formatedMsg; 82 } 83 free(formatedMsg); 84 85 va_end(args); 86 } 87 std::string errors; 88 }; 89 90 ::testing::AssertionResult validateXml(const char* xmlFilePathExpr, 91 const char* xsdFilePathExpr, 92 const char* xmlFilePath, 93 const char* xsdFilePath) { 94 Libxml2Global libxml2; 95 96 auto context = [&]() { 97 return std::string() + " While validating: " + xmlFilePathExpr + 98 "\n Which is: " + xmlFilePath + 99 "\nAgainst the schema: " + xsdFilePathExpr + 100 "\n Which is: " + xsdFilePath + "Libxml2 errors\n" + 101 libxml2.getErrors(); 102 }; 103 104 auto schemaParserCtxt = make_xmlUnique(xmlSchemaNewParserCtxt(xsdFilePath)); 105 auto schema = make_xmlUnique(xmlSchemaParse(schemaParserCtxt.get())); 106 if (schema == nullptr) { 107 return ::testing::AssertionFailure() << "Failed to parse schema (xsd)\n" 108 << context(); 109 } 110 111 auto doc = make_xmlUnique(xmlReadFile(xmlFilePath, nullptr, 0)); 112 if (doc == nullptr) { 113 return ::testing::AssertionFailure() << "Failed to parse xml\n" 114 << context(); 115 } 116 117 if (xmlXIncludeProcess(doc.get()) == -1) { 118 return ::testing::AssertionFailure() 119 << "Failed to resolve xincludes in xml\n" 120 << context(); 121 } 122 123 auto schemaCtxt = make_xmlUnique(xmlSchemaNewValidCtxt(schema.get())); 124 int ret = xmlSchemaValidateDoc(schemaCtxt.get(), doc.get()); 125 if (ret > 0) { 126 return ::testing::AssertionFailure() 127 << "xml is not valid according to the xsd.\n" 128 << context(); 129 } 130 if (ret < 0) { 131 return ::testing::AssertionFailure() << "Internal or API error\n" 132 << context(); 133 } 134 135 return ::testing::AssertionSuccess(); 136 } 137 138 } // namespace test 139 } // namespace audio 140 } // namespace hardware 141 } // namespace android 142