| /*------------------------------------------------------------------------- |
| * drawElements Quality Program Test Executor |
| * ------------------------------------------ |
| * |
| * Copyright 2014 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. |
| * |
| *//*! |
| * \file |
| * \brief Test case result parser. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "xeTestResultParser.hpp" |
| #include "xeTestCaseResult.hpp" |
| #include "xeBatchResult.hpp" |
| #include "deString.h" |
| #include "deInt32.h" |
| |
| #include <sstream> |
| #include <stdlib.h> |
| |
| using std::string; |
| using std::vector; |
| |
| namespace xe |
| { |
| |
| static inline int toInt(const char *str) |
| { |
| return atoi(str); |
| } |
| |
| static inline double toDouble(const char *str) |
| { |
| return atof(str); |
| } |
| |
| static inline int64_t toInt64(const char *str) |
| { |
| std::istringstream s(str); |
| int64_t val; |
| |
| s >> val; |
| |
| return val; |
| } |
| |
| static inline bool toBool(const char *str) |
| { |
| return deStringEqual(str, "OK") || deStringEqual(str, "True"); |
| } |
| |
| static const char *stripLeadingWhitespace(const char *str) |
| { |
| int whitespaceCount = 0; |
| |
| while (str[whitespaceCount] != 0 && (str[whitespaceCount] == ' ' || str[whitespaceCount] == '\t' || |
| str[whitespaceCount] == '\r' || str[whitespaceCount] == '\n')) |
| whitespaceCount += 1; |
| |
| return str + whitespaceCount; |
| } |
| |
| struct EnumMapEntry |
| { |
| uint32_t hash; |
| const char *name; |
| int value; |
| }; |
| |
| static const EnumMapEntry s_statusCodeMap[] = { |
| {0x7c8a99bc, "Pass", TESTSTATUSCODE_PASS}, |
| {0x7c851ca1, "Fail", TESTSTATUSCODE_FAIL}, |
| {0x10ecd324, "QualityWarning", TESTSTATUSCODE_QUALITY_WARNING}, |
| {0x341ae835, "CompatibilityWarning", TESTSTATUSCODE_COMPATIBILITY_WARNING}, |
| {0x058acbca, "Pending", TESTSTATUSCODE_PENDING}, |
| {0xc4d74b26, "Running", TESTSTATUSCODE_RUNNING}, |
| {0x6409f93c, "NotSupported", TESTSTATUSCODE_NOT_SUPPORTED}, |
| {0xfa5a9ab7, "ResourceError", TESTSTATUSCODE_RESOURCE_ERROR}, |
| {0xad6793ec, "InternalError", TESTSTATUSCODE_INTERNAL_ERROR}, |
| {0x838f3034, "Canceled", TESTSTATUSCODE_CANCELED}, |
| {0x42b6efac, "Timeout", TESTSTATUSCODE_TIMEOUT}, |
| {0x0cfb98f6, "Crash", TESTSTATUSCODE_CRASH}, |
| {0xe326e01d, "Disabled", TESTSTATUSCODE_DISABLED}, |
| {0x77061af2, "Terminated", TESTSTATUSCODE_TERMINATED}, |
| {0xd9e6b393, "Waiver", TESTSTATUSCODE_WAIVER}}; |
| |
| static const EnumMapEntry s_resultItemMap[] = {{0xce8ac2e4, "Result", ri::TYPE_RESULT}, |
| {0x7c8cdcea, "Text", ri::TYPE_TEXT}, |
| {0xc6540c6e, "Number", ri::TYPE_NUMBER}, |
| {0x0d656c88, "Image", ri::TYPE_IMAGE}, |
| {0x8ac9ee14, "ImageSet", ri::TYPE_IMAGESET}, |
| {0x1181fa5a, "VertexShader", ri::TYPE_SHADER}, |
| {0xa93daef0, "FragmentShader", ri::TYPE_SHADER}, |
| {0x8f066128, "GeometryShader", ri::TYPE_SHADER}, |
| {0x235a931c, "TessControlShader", ri::TYPE_SHADER}, |
| {0xa1bf7153, "TessEvaluationShader", ri::TYPE_SHADER}, |
| {0x6c1415d9, "ComputeShader", ri::TYPE_SHADER}, |
| {0x68738b22, "RaygenShader", ri::TYPE_SHADER}, |
| {0x51d29ce9, "AnyHitShader", ri::TYPE_SHADER}, |
| {0x8c64a6be, "ClosestHitShader", ri::TYPE_SHADER}, |
| {0xb30ed398, "MissShader", ri::TYPE_SHADER}, |
| {0x26150e53, "IntersectionShader", ri::TYPE_SHADER}, |
| {0x7e50944c, "CallableShader", ri::TYPE_SHADER}, |
| {0x72863a54, "ShaderProgram", ri::TYPE_SHADERPROGRAM}, |
| {0xb4efc08d, "ShaderSource", ri::TYPE_SHADERSOURCE}, |
| {0xaee4380a, "SpirVAssemblySource", ri::TYPE_SPIRVSOURCE}, |
| {0xff265913, "InfoLog", ri::TYPE_INFOLOG}, |
| {0x84159b73, "EglConfig", ri::TYPE_EGLCONFIG}, |
| {0xdd34391f, "EglConfigSet", ri::TYPE_EGLCONFIGSET}, |
| {0xebbb3aba, "Section", ri::TYPE_SECTION}, |
| {0xa0f15677, "KernelSource", ri::TYPE_KERNELSOURCE}, |
| {0x1ee9083a, "CompileInfo", ri::TYPE_COMPILEINFO}, |
| {0xf1004023, "SampleList", ri::TYPE_SAMPLELIST}, |
| {0xf0feae93, "SampleInfo", ri::TYPE_SAMPLEINFO}, |
| {0x2aa6f14e, "ValueInfo", ri::TYPE_VALUEINFO}, |
| {0xd09429e7, "Sample", ri::TYPE_SAMPLE}, |
| {0x0e4a4722, "Value", ri::TYPE_SAMPLEVALUE}}; |
| |
| static const EnumMapEntry s_imageFormatMap[] = {{0xcc4ffac8, "RGB888", ri::Image::FORMAT_RGB888}, |
| {0x20dcb0c1, "RGBA8888", ri::Image::FORMAT_RGBA8888}}; |
| |
| static const EnumMapEntry s_compressionMap[] = {{0x7c89bbd5, "None", ri::Image::COMPRESSION_NONE}, |
| {0x0b88118a, "PNG", ri::Image::COMPRESSION_PNG}}; |
| |
| static const EnumMapEntry s_shaderTypeFromTagMap[] = { |
| {0x1181fa5a, "VertexShader", ri::Shader::SHADERTYPE_VERTEX}, |
| {0xa93daef0, "FragmentShader", ri::Shader::SHADERTYPE_FRAGMENT}, |
| {0x8f066128, "GeometryShader", ri::Shader::SHADERTYPE_GEOMETRY}, |
| {0x235a931c, "TessControlShader", ri::Shader::SHADERTYPE_TESS_CONTROL}, |
| {0xa1bf7153, "TessEvaluationShader", ri::Shader::SHADERTYPE_TESS_EVALUATION}, |
| {0x6c1415d9, "ComputeShader", ri::Shader::SHADERTYPE_COMPUTE}, |
| {0x68738b22, "RaygenShader", ri::Shader::SHADERTYPE_RAYGEN}, |
| {0x51d29ce9, "AnyHitShader", ri::Shader::SHADERTYPE_ANY_HIT}, |
| {0x8c64a6be, "ClosestHitShader", ri::Shader::SHADERTYPE_CLOSEST_HIT}, |
| {0xb30ed398, "MissShader", ri::Shader::SHADERTYPE_MISS}, |
| {0x26150e53, "IntersectionShader", ri::Shader::SHADERTYPE_INTERSECTION}, |
| {0x7e50944c, "CallableShader", ri::Shader::SHADERTYPE_CALLABLE}}; |
| |
| static const EnumMapEntry s_testTypeMap[] = {{0x7fa80959, "SelfValidate", TESTCASETYPE_SELF_VALIDATE}, |
| {0xdb797567, "Capability", TESTCASETYPE_CAPABILITY}, |
| {0x2ca3ec10, "Accuracy", TESTCASETYPE_ACCURACY}, |
| {0xa48ac277, "Performance", TESTCASETYPE_PERFORMANCE}}; |
| |
| static const EnumMapEntry s_logVersionMap[] = { |
| {0x0b7dac93, "0.2.0", TESTLOGVERSION_0_2_0}, {0x0b7db0d4, "0.3.0", TESTLOGVERSION_0_3_0}, |
| {0x0b7db0d5, "0.3.1", TESTLOGVERSION_0_3_1}, {0x0b7db0d6, "0.3.2", TESTLOGVERSION_0_3_2}, |
| {0x0b7db0d7, "0.3.3", TESTLOGVERSION_0_3_3}, {0x0b7db0d8, "0.3.4", TESTLOGVERSION_0_3_4}}; |
| |
| static const EnumMapEntry s_sampleValueTagMap[] = { |
| {0xddf2d0d1, "Predictor", ri::ValueInfo::VALUETAG_PREDICTOR}, |
| {0x9bee2c34, "Response", ri::ValueInfo::VALUETAG_RESPONSE}, |
| }; |
| |
| #if defined(DE_DEBUG) |
| static void printHashes(const char *name, const EnumMapEntry *entries, int numEntries) |
| { |
| printf("%s:\n", name); |
| |
| for (int ndx = 0; ndx < numEntries; ndx++) |
| printf("0x%08x\t%s\n", deStringHash(entries[ndx].name), entries[ndx].name); |
| |
| printf("\n"); |
| } |
| |
| #define PRINT_HASHES(MAP) printHashes(#MAP, MAP, DE_LENGTH_OF_ARRAY(MAP)) |
| |
| void TestResultParser_printHashes(void) |
| { |
| PRINT_HASHES(s_statusCodeMap); |
| PRINT_HASHES(s_resultItemMap); |
| PRINT_HASHES(s_imageFormatMap); |
| PRINT_HASHES(s_compressionMap); |
| PRINT_HASHES(s_shaderTypeFromTagMap); |
| PRINT_HASHES(s_testTypeMap); |
| PRINT_HASHES(s_logVersionMap); |
| PRINT_HASHES(s_sampleValueTagMap); |
| } |
| #endif |
| |
| static inline int getEnumValue(const char *enumName, const EnumMapEntry *entries, int numEntries, const char *name) |
| { |
| uint32_t hash = deStringHash(name); |
| |
| for (int ndx = 0; ndx < numEntries; ndx++) |
| { |
| if (entries[ndx].hash == hash && deStringEqual(entries[ndx].name, name)) |
| return entries[ndx].value; |
| } |
| |
| throw TestResultParseError(string("Could not map '") + name + "' to " + enumName); |
| } |
| |
| TestStatusCode getTestStatusCode(const char *statusCode) |
| { |
| return (TestStatusCode)getEnumValue("status code", s_statusCodeMap, DE_LENGTH_OF_ARRAY(s_statusCodeMap), |
| statusCode); |
| } |
| |
| static ri::Type getResultItemType(const char *elemName) |
| { |
| return (ri::Type)getEnumValue("result item type", s_resultItemMap, DE_LENGTH_OF_ARRAY(s_resultItemMap), elemName); |
| } |
| |
| static ri::Image::Format getImageFormat(const char *imageFormat) |
| { |
| return (ri::Image::Format)getEnumValue("image format", s_imageFormatMap, DE_LENGTH_OF_ARRAY(s_imageFormatMap), |
| imageFormat); |
| } |
| |
| static ri::Image::Compression getImageCompression(const char *compression) |
| { |
| return (ri::Image::Compression)getEnumValue("image compression", s_compressionMap, |
| DE_LENGTH_OF_ARRAY(s_compressionMap), compression); |
| } |
| |
| static ri::Shader::ShaderType getShaderTypeFromTagName(const char *shaderType) |
| { |
| return (ri::Shader::ShaderType)getEnumValue("shader type", s_shaderTypeFromTagMap, |
| DE_LENGTH_OF_ARRAY(s_shaderTypeFromTagMap), shaderType); |
| } |
| |
| static TestCaseType getTestCaseType(const char *caseType) |
| { |
| return (TestCaseType)getEnumValue("test case type", s_testTypeMap, DE_LENGTH_OF_ARRAY(s_testTypeMap), caseType); |
| } |
| |
| static TestLogVersion getTestLogVersion(const char *logVersion) |
| { |
| return (TestLogVersion)getEnumValue("test log version", s_logVersionMap, DE_LENGTH_OF_ARRAY(s_logVersionMap), |
| logVersion); |
| } |
| |
| static ri::ValueInfo::ValueTag getSampleValueTag(const char *tag) |
| { |
| return (ri::ValueInfo::ValueTag)getEnumValue("sample value tag", s_sampleValueTagMap, |
| DE_LENGTH_OF_ARRAY(s_sampleValueTagMap), tag); |
| } |
| |
| static TestCaseType getTestCaseTypeFromPath(const char *casePath) |
| { |
| if (deStringBeginsWith(casePath, "dEQP-GLES2.")) |
| { |
| const char *group = casePath + 11; |
| if (deStringBeginsWith(group, "capability.")) |
| return TESTCASETYPE_CAPABILITY; |
| else if (deStringBeginsWith(group, "accuracy.")) |
| return TESTCASETYPE_ACCURACY; |
| else if (deStringBeginsWith(group, "performance.")) |
| return TESTCASETYPE_PERFORMANCE; |
| } |
| |
| return TESTCASETYPE_SELF_VALIDATE; |
| } |
| |
| static ri::NumericValue getNumericValue(const std::string &value) |
| { |
| const bool isFloat = value.find('.') != std::string::npos || value.find('e') != std::string::npos; |
| |
| if (isFloat) |
| { |
| const double num = toDouble(stripLeadingWhitespace(value.c_str())); |
| return ri::NumericValue(num); |
| } |
| else |
| { |
| const int64_t num = toInt64(stripLeadingWhitespace(value.c_str())); |
| return ri::NumericValue(num); |
| } |
| } |
| |
| TestResultParser::TestResultParser(void) |
| : m_result(DE_NULL) |
| , m_state(STATE_NOT_INITIALIZED) |
| , m_logVersion(TESTLOGVERSION_LAST) |
| , m_curItemList(DE_NULL) |
| , m_base64DecodeOffset(0) |
| { |
| } |
| |
| TestResultParser::~TestResultParser(void) |
| { |
| } |
| |
| void TestResultParser::clear(void) |
| { |
| m_xmlParser.clear(); |
| m_itemStack.clear(); |
| |
| m_result = DE_NULL; |
| m_state = STATE_NOT_INITIALIZED; |
| m_logVersion = TESTLOGVERSION_LAST; |
| m_curItemList = DE_NULL; |
| m_base64DecodeOffset = 0; |
| m_curNumValue.clear(); |
| } |
| |
| void TestResultParser::init(TestCaseResult *dstResult) |
| { |
| clear(); |
| m_result = dstResult; |
| m_state = STATE_INITIALIZED; |
| m_curItemList = &dstResult->resultItems; |
| } |
| |
| TestResultParser::ParseResult TestResultParser::parse(const uint8_t *bytes, int numBytes) |
| { |
| DE_ASSERT(m_result && m_state != STATE_NOT_INITIALIZED); |
| |
| try |
| { |
| bool resultChanged = false; |
| |
| m_xmlParser.feed(bytes, numBytes); |
| |
| for (;;) |
| { |
| xml::Element curElement = m_xmlParser.getElement(); |
| |
| if (curElement == xml::ELEMENT_INCOMPLETE || curElement == xml::ELEMENT_END_OF_STRING) |
| break; |
| |
| switch (curElement) |
| { |
| case xml::ELEMENT_START: |
| handleElementStart(); |
| break; |
| case xml::ELEMENT_END: |
| handleElementEnd(); |
| break; |
| case xml::ELEMENT_DATA: |
| handleData(); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| |
| resultChanged = true; |
| m_xmlParser.advance(); |
| } |
| |
| if (m_xmlParser.getElement() == xml::ELEMENT_END_OF_STRING) |
| { |
| if (m_state != STATE_TEST_CASE_RESULT_ENDED) |
| throw TestResultParseError("Unexpected end of log data"); |
| |
| return PARSERESULT_COMPLETE; |
| } |
| else |
| return resultChanged ? PARSERESULT_CHANGED : PARSERESULT_NOT_CHANGED; |
| } |
| catch (const TestResultParseError &e) |
| { |
| // Set error code to result. |
| m_result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR; |
| m_result->statusDetails = e.what(); |
| |
| return PARSERESULT_ERROR; |
| } |
| catch (const xml::ParseError &e) |
| { |
| // Set error code to result. |
| m_result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR; |
| m_result->statusDetails = e.what(); |
| |
| return PARSERESULT_ERROR; |
| } |
| } |
| |
| const char *TestResultParser::getAttribute(const char *name) |
| { |
| if (!m_xmlParser.hasAttribute(name)) |
| throw TestResultParseError(string("Missing attribute '") + name + "' in <" + m_xmlParser.getElementName() + |
| ">"); |
| |
| return m_xmlParser.getAttribute(name); |
| } |
| |
| ri::Item *TestResultParser::getCurrentItem(void) |
| { |
| return !m_itemStack.empty() ? m_itemStack.back() : DE_NULL; |
| } |
| |
| ri::List *TestResultParser::getCurrentItemList(void) |
| { |
| DE_ASSERT(m_curItemList); |
| return m_curItemList; |
| } |
| |
| void TestResultParser::updateCurrentItemList(void) |
| { |
| m_curItemList = DE_NULL; |
| |
| for (vector<ri::Item *>::reverse_iterator i = m_itemStack.rbegin(); i != m_itemStack.rend(); i++) |
| { |
| ri::Item *item = *i; |
| ri::Type type = item->getType(); |
| |
| if (type == ri::TYPE_IMAGESET) |
| m_curItemList = &static_cast<ri::ImageSet *>(item)->images; |
| else if (type == ri::TYPE_SECTION) |
| m_curItemList = &static_cast<ri::Section *>(item)->items; |
| else if (type == ri::TYPE_EGLCONFIGSET) |
| m_curItemList = &static_cast<ri::EglConfigSet *>(item)->configs; |
| else if (type == ri::TYPE_SHADERPROGRAM) |
| m_curItemList = &static_cast<ri::ShaderProgram *>(item)->shaders; |
| |
| if (m_curItemList) |
| break; |
| } |
| |
| if (!m_curItemList) |
| m_curItemList = &m_result->resultItems; |
| } |
| |
| void TestResultParser::pushItem(ri::Item *item) |
| { |
| m_itemStack.push_back(item); |
| updateCurrentItemList(); |
| } |
| |
| void TestResultParser::popItem(void) |
| { |
| m_itemStack.pop_back(); |
| updateCurrentItemList(); |
| } |
| |
| void TestResultParser::handleElementStart(void) |
| { |
| const char *elemName = m_xmlParser.getElementName(); |
| |
| if (m_state == STATE_INITIALIZED) |
| { |
| // Expect TestCaseResult. |
| if (!deStringEqual(elemName, "TestCaseResult")) |
| throw TestResultParseError(string("Expected <TestCaseResult>, got <") + elemName + ">"); |
| |
| const char *version = getAttribute("Version"); |
| m_logVersion = getTestLogVersion(version); |
| // \note Currently assumed that all known log versions are supported. |
| |
| m_result->casePath = getAttribute("CasePath"); |
| m_result->caseType = TESTCASETYPE_SELF_VALIDATE; |
| |
| if (m_xmlParser.hasAttribute("CaseType")) |
| m_result->caseType = getTestCaseType(m_xmlParser.getAttribute("CaseType")); |
| else |
| { |
| // Do guess based on path for legacy log files. |
| if (m_logVersion >= TESTLOGVERSION_0_3_2) |
| throw TestResultParseError("Missing CaseType attribute in <TestCaseResult>"); |
| m_result->caseType = getTestCaseTypeFromPath(m_result->casePath.c_str()); |
| } |
| |
| m_state = STATE_IN_TEST_CASE_RESULT; |
| } |
| else |
| { |
| ri::List *curList = getCurrentItemList(); |
| ri::Type itemType = getResultItemType(elemName); |
| ri::Item *item = DE_NULL; |
| ri::Item *parentItem = getCurrentItem(); |
| ri::Type parentType = parentItem ? parentItem->getType() : ri::TYPE_LAST; |
| |
| switch (itemType) |
| { |
| case ri::TYPE_RESULT: |
| { |
| ri::Result *result = curList->allocItem<ri::Result>(); |
| result->statusCode = getTestStatusCode(getAttribute("StatusCode")); |
| item = result; |
| break; |
| } |
| |
| case ri::TYPE_TEXT: |
| item = curList->allocItem<ri::Text>(); |
| break; |
| |
| case ri::TYPE_SECTION: |
| { |
| ri::Section *section = curList->allocItem<ri::Section>(); |
| section->name = getAttribute("Name"); |
| section->description = getAttribute("Description"); |
| item = section; |
| break; |
| } |
| |
| case ri::TYPE_NUMBER: |
| { |
| ri::Number *number = curList->allocItem<ri::Number>(); |
| number->name = getAttribute("Name"); |
| number->description = getAttribute("Description"); |
| number->unit = getAttribute("Unit"); |
| |
| if (m_xmlParser.hasAttribute("Tag")) |
| number->tag = m_xmlParser.getAttribute("Tag"); |
| |
| item = number; |
| |
| m_curNumValue.clear(); |
| break; |
| } |
| |
| case ri::TYPE_IMAGESET: |
| { |
| ri::ImageSet *imageSet = curList->allocItem<ri::ImageSet>(); |
| imageSet->name = getAttribute("Name"); |
| imageSet->description = getAttribute("Description"); |
| item = imageSet; |
| break; |
| } |
| |
| case ri::TYPE_IMAGE: |
| { |
| ri::Image *image = curList->allocItem<ri::Image>(); |
| image->name = getAttribute("Name"); |
| image->description = getAttribute("Description"); |
| image->width = toInt(getAttribute("Width")); |
| image->height = toInt(getAttribute("Height")); |
| image->format = getImageFormat(getAttribute("Format")); |
| image->compression = getImageCompression(getAttribute("CompressionMode")); |
| item = image; |
| break; |
| } |
| |
| case ri::TYPE_SHADERPROGRAM: |
| { |
| ri::ShaderProgram *shaderProgram = curList->allocItem<ri::ShaderProgram>(); |
| shaderProgram->linkStatus = toBool(getAttribute("LinkStatus")); |
| item = shaderProgram; |
| break; |
| } |
| |
| case ri::TYPE_SHADER: |
| { |
| if (parentType != ri::TYPE_SHADERPROGRAM) |
| throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>"); |
| |
| ri::Shader *shader = curList->allocItem<ri::Shader>(); |
| |
| shader->shaderType = getShaderTypeFromTagName(elemName); |
| shader->compileStatus = toBool(getAttribute("CompileStatus")); |
| |
| item = shader; |
| break; |
| } |
| |
| case ri::TYPE_SPIRVSOURCE: |
| { |
| if (parentType != ri::TYPE_SHADERPROGRAM) |
| throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>"); |
| item = curList->allocItem<ri::SpirVSource>(); |
| break; |
| } |
| |
| case ri::TYPE_SHADERSOURCE: |
| if (parentType == ri::TYPE_SHADER) |
| item = &static_cast<ri::Shader *>(parentItem)->source; |
| else |
| throw TestResultParseError("Unexpected <ShaderSource>"); |
| break; |
| |
| case ri::TYPE_INFOLOG: |
| if (parentType == ri::TYPE_SHADERPROGRAM) |
| item = &static_cast<ri::ShaderProgram *>(parentItem)->linkInfoLog; |
| else if (parentType == ri::TYPE_SHADER) |
| item = &static_cast<ri::Shader *>(parentItem)->infoLog; |
| else if (parentType == ri::TYPE_COMPILEINFO) |
| item = &static_cast<ri::CompileInfo *>(parentItem)->infoLog; |
| else |
| throw TestResultParseError("Unexpected <InfoLog>"); |
| break; |
| |
| case ri::TYPE_KERNELSOURCE: |
| item = curList->allocItem<ri::KernelSource>(); |
| break; |
| |
| case ri::TYPE_COMPILEINFO: |
| { |
| ri::CompileInfo *info = curList->allocItem<ri::CompileInfo>(); |
| info->name = getAttribute("Name"); |
| info->description = getAttribute("Description"); |
| info->compileStatus = toBool(getAttribute("CompileStatus")); |
| item = info; |
| break; |
| } |
| |
| case ri::TYPE_EGLCONFIGSET: |
| { |
| ri::EglConfigSet *set = curList->allocItem<ri::EglConfigSet>(); |
| set->name = getAttribute("Name"); |
| set->description = m_xmlParser.hasAttribute("Description") ? m_xmlParser.getAttribute("Description") : ""; |
| item = set; |
| break; |
| } |
| |
| case ri::TYPE_EGLCONFIG: |
| { |
| ri::EglConfig *config = curList->allocItem<ri::EglConfig>(); |
| config->bufferSize = toInt(getAttribute("BufferSize")); |
| config->redSize = toInt(getAttribute("RedSize")); |
| config->greenSize = toInt(getAttribute("GreenSize")); |
| config->blueSize = toInt(getAttribute("BlueSize")); |
| config->luminanceSize = toInt(getAttribute("LuminanceSize")); |
| config->alphaSize = toInt(getAttribute("AlphaSize")); |
| config->alphaMaskSize = toInt(getAttribute("AlphaMaskSize")); |
| config->bindToTextureRGB = toBool(getAttribute("BindToTextureRGB")); |
| config->bindToTextureRGBA = toBool(getAttribute("BindToTextureRGBA")); |
| config->colorBufferType = getAttribute("ColorBufferType"); |
| config->configCaveat = getAttribute("ConfigCaveat"); |
| config->configID = toInt(getAttribute("ConfigID")); |
| config->conformant = getAttribute("Conformant"); |
| config->depthSize = toInt(getAttribute("DepthSize")); |
| config->level = toInt(getAttribute("Level")); |
| config->maxPBufferWidth = toInt(getAttribute("MaxPBufferWidth")); |
| config->maxPBufferHeight = toInt(getAttribute("MaxPBufferHeight")); |
| config->maxPBufferPixels = toInt(getAttribute("MaxPBufferPixels")); |
| config->maxSwapInterval = toInt(getAttribute("MaxSwapInterval")); |
| config->minSwapInterval = toInt(getAttribute("MinSwapInterval")); |
| config->nativeRenderable = toBool(getAttribute("NativeRenderable")); |
| config->renderableType = getAttribute("RenderableType"); |
| config->sampleBuffers = toInt(getAttribute("SampleBuffers")); |
| config->samples = toInt(getAttribute("Samples")); |
| config->stencilSize = toInt(getAttribute("StencilSize")); |
| config->surfaceTypes = getAttribute("SurfaceTypes"); |
| config->transparentType = getAttribute("TransparentType"); |
| config->transparentRedValue = toInt(getAttribute("TransparentRedValue")); |
| config->transparentGreenValue = toInt(getAttribute("TransparentGreenValue")); |
| config->transparentBlueValue = toInt(getAttribute("TransparentBlueValue")); |
| item = config; |
| break; |
| } |
| |
| case ri::TYPE_SAMPLELIST: |
| { |
| ri::SampleList *list = curList->allocItem<ri::SampleList>(); |
| list->name = getAttribute("Name"); |
| list->description = getAttribute("Description"); |
| item = list; |
| break; |
| } |
| |
| case ri::TYPE_SAMPLEINFO: |
| { |
| if (parentType != ri::TYPE_SAMPLELIST) |
| throw TestResultParseError("<SampleInfo> outside of <SampleList>"); |
| |
| ri::SampleList *list = static_cast<ri::SampleList *>(parentItem); |
| ri::SampleInfo *info = &list->sampleInfo; |
| |
| item = info; |
| break; |
| } |
| |
| case ri::TYPE_VALUEINFO: |
| { |
| if (parentType != ri::TYPE_SAMPLEINFO) |
| throw TestResultParseError("<ValueInfo> outside of <SampleInfo>"); |
| |
| ri::SampleInfo *sampleInfo = static_cast<ri::SampleInfo *>(parentItem); |
| ri::ValueInfo *valueInfo = sampleInfo->valueInfos.allocItem<ri::ValueInfo>(); |
| |
| valueInfo->name = getAttribute("Name"); |
| valueInfo->description = getAttribute("Description"); |
| valueInfo->tag = getSampleValueTag(getAttribute("Tag")); |
| |
| if (m_xmlParser.hasAttribute("Unit")) |
| valueInfo->unit = getAttribute("Unit"); |
| |
| item = valueInfo; |
| break; |
| } |
| |
| case ri::TYPE_SAMPLE: |
| { |
| if (parentType != ri::TYPE_SAMPLELIST) |
| throw TestResultParseError("<Sample> outside of <SampleList>"); |
| |
| ri::SampleList *list = static_cast<ri::SampleList *>(parentItem); |
| ri::Sample *sample = list->samples.allocItem<ri::Sample>(); |
| |
| item = sample; |
| break; |
| } |
| |
| case ri::TYPE_SAMPLEVALUE: |
| { |
| if (parentType != ri::TYPE_SAMPLE) |
| throw TestResultParseError("<Value> outside of <Sample>"); |
| |
| ri::Sample *sample = static_cast<ri::Sample *>(parentItem); |
| ri::SampleValue *value = sample->values.allocItem<ri::SampleValue>(); |
| |
| item = value; |
| break; |
| } |
| |
| default: |
| throw TestResultParseError(string("Unsupported element '") + elemName + ("'")); |
| } |
| |
| DE_ASSERT(item); |
| pushItem(item); |
| |
| // Reset base64 decoding offset. |
| m_base64DecodeOffset = 0; |
| } |
| } |
| |
| void TestResultParser::handleElementEnd(void) |
| { |
| const char *elemName = m_xmlParser.getElementName(); |
| |
| if (m_state != STATE_IN_TEST_CASE_RESULT) |
| throw TestResultParseError(string("Unexpected </") + elemName + "> outside of <TestCaseResult>"); |
| |
| if (deStringEqual(elemName, "TestCaseResult")) |
| { |
| // Logs from buggy test cases may contain invalid XML. |
| // DE_ASSERT(getCurrentItem() == DE_NULL); |
| // \todo [2012-11-22 pyry] Log warning. |
| |
| m_state = STATE_TEST_CASE_RESULT_ENDED; |
| } |
| else |
| { |
| ri::Type itemType = getResultItemType(elemName); |
| ri::Item *curItem = getCurrentItem(); |
| |
| if (!curItem || itemType != curItem->getType()) |
| throw TestResultParseError(string("Unexpected </") + elemName + ">"); |
| |
| if (itemType == ri::TYPE_RESULT) |
| { |
| ri::Result *result = static_cast<ri::Result *>(curItem); |
| m_result->statusCode = result->statusCode; |
| m_result->statusDetails = result->details; |
| } |
| else if (itemType == ri::TYPE_NUMBER) |
| { |
| // Parse value for number. |
| ri::Number *number = static_cast<ri::Number *>(curItem); |
| number->value = getNumericValue(m_curNumValue); |
| m_curNumValue.clear(); |
| } |
| else if (itemType == ri::TYPE_SAMPLEVALUE) |
| { |
| ri::SampleValue *value = static_cast<ri::SampleValue *>(curItem); |
| value->value = getNumericValue(m_curNumValue); |
| m_curNumValue.clear(); |
| } |
| |
| popItem(); |
| } |
| } |
| |
| void TestResultParser::handleData(void) |
| { |
| ri::Item *curItem = getCurrentItem(); |
| ri::Type type = curItem ? curItem->getType() : ri::TYPE_LAST; |
| |
| switch (type) |
| { |
| case ri::TYPE_RESULT: |
| m_xmlParser.appendDataStr(static_cast<ri::Result *>(curItem)->details); |
| break; |
| |
| case ri::TYPE_TEXT: |
| m_xmlParser.appendDataStr(static_cast<ri::Text *>(curItem)->text); |
| break; |
| |
| case ri::TYPE_SHADERSOURCE: |
| m_xmlParser.appendDataStr(static_cast<ri::ShaderSource *>(curItem)->source); |
| break; |
| |
| case ri::TYPE_SPIRVSOURCE: |
| m_xmlParser.appendDataStr(static_cast<ri::SpirVSource *>(curItem)->source); |
| break; |
| |
| case ri::TYPE_INFOLOG: |
| m_xmlParser.appendDataStr(static_cast<ri::InfoLog *>(curItem)->log); |
| break; |
| |
| case ri::TYPE_KERNELSOURCE: |
| m_xmlParser.appendDataStr(static_cast<ri::KernelSource *>(curItem)->source); |
| break; |
| |
| case ri::TYPE_NUMBER: |
| case ri::TYPE_SAMPLEVALUE: |
| m_xmlParser.appendDataStr(m_curNumValue); |
| break; |
| |
| case ri::TYPE_IMAGE: |
| { |
| ri::Image *image = static_cast<ri::Image *>(curItem); |
| |
| // Base64 decode. |
| int numBytesIn = m_xmlParser.getDataSize(); |
| |
| for (int inNdx = 0; inNdx < numBytesIn; inNdx++) |
| { |
| uint8_t byte = m_xmlParser.getDataByte(inNdx); |
| uint8_t decodedBits = 0; |
| |
| if (de::inRange<int8_t>(byte, 'A', 'Z')) |
| decodedBits = (uint8_t)(byte - 'A'); |
| else if (de::inRange<int8_t>(byte, 'a', 'z')) |
| decodedBits = (uint8_t)(('Z' - 'A' + 1) + (byte - 'a')); |
| else if (de::inRange<int8_t>(byte, '0', '9')) |
| decodedBits = (uint8_t)(('Z' - 'A' + 1) + ('z' - 'a' + 1) + (byte - '0')); |
| else if (byte == '+') |
| decodedBits = ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 1); |
| else if (byte == '/') |
| decodedBits = ('Z' - 'A' + 1) + ('z' - 'a' + 1) + ('9' - '0' + 2); |
| else if (byte == '=') |
| { |
| // Padding at end - remove last byte. |
| if (image->data.empty()) |
| throw TestResultParseError("Malformed base64 data"); |
| image->data.pop_back(); |
| continue; |
| } |
| else |
| continue; // Not an B64 input character. |
| |
| int phase = m_base64DecodeOffset % 4; |
| |
| if (phase == 0) |
| image->data.resize(image->data.size() + 3, 0); |
| |
| if ((int)image->data.size() < (m_base64DecodeOffset >> 2) * 3 + 3) |
| throw TestResultParseError("Malformed base64 data"); |
| uint8_t *outPtr = &image->data[(m_base64DecodeOffset >> 2) * 3]; |
| |
| switch (phase) |
| { |
| case 0: |
| outPtr[0] |= (uint8_t)(decodedBits << 2); |
| break; |
| case 1: |
| outPtr[0] = (uint8_t)(outPtr[0] | (uint8_t)(decodedBits >> 4)); |
| outPtr[1] = (uint8_t)(outPtr[1] | (uint8_t)((decodedBits & 0xF) << 4)); |
| break; |
| case 2: |
| outPtr[1] = (uint8_t)(outPtr[1] | (uint8_t)(decodedBits >> 2)); |
| outPtr[2] = (uint8_t)(outPtr[2] | (uint8_t)((decodedBits & 0x3) << 6)); |
| break; |
| case 3: |
| outPtr[2] |= decodedBits; |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| |
| m_base64DecodeOffset += 1; |
| } |
| |
| break; |
| } |
| |
| default: |
| // Just ignore data. |
| break; |
| } |
| } |
| |
| //! Helper for parsing TestCaseResult from TestCaseResultData. |
| void parseTestCaseResultFromData(TestResultParser *parser, TestCaseResult *result, const TestCaseResultData &data) |
| { |
| DE_ASSERT(result->resultItems.getNumItems() == 0); |
| |
| // Initialize status codes etc. from data. |
| result->casePath = data.getTestCasePath(); |
| result->caseType = TESTCASETYPE_SELF_VALIDATE; |
| result->statusCode = data.getStatusCode(); |
| result->statusDetails = data.getStatusDetails(); |
| |
| if (data.getDataSize() > 0) |
| { |
| parser->init(result); |
| |
| const TestResultParser::ParseResult parseResult = parser->parse(data.getData(), data.getDataSize()); |
| |
| if (result->statusCode == TESTSTATUSCODE_LAST) |
| { |
| result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR; |
| |
| if (parseResult == TestResultParser::PARSERESULT_ERROR) |
| result->statusDetails = "Test case result parsing failed"; |
| else if (parseResult != TestResultParser::PARSERESULT_COMPLETE) |
| result->statusDetails = "Incomplete test case result"; |
| else |
| result->statusDetails = "Test case result is missing <Result> item"; |
| } |
| } |
| else if (result->statusCode == TESTSTATUSCODE_LAST) |
| { |
| result->statusCode = TESTSTATUSCODE_TERMINATED; |
| result->statusDetails = "Empty test case result"; |
| } |
| |
| if (result->casePath.empty()) |
| throw Error("Empty test case path in result"); |
| |
| if (result->caseType == TESTCASETYPE_LAST) |
| throw Error("Invalid test case type in result"); |
| |
| DE_ASSERT(result->statusCode != TESTSTATUSCODE_LAST); |
| } |
| |
| } // namespace xe |