Import dEQP.
Import drawElements Quality Program from an internal repository.
Bug: 17388917
Change-Id: Ic109fe4a57e31b2a816113d90fbdf51a43e7abeb
diff --git a/executor/xeTestResultParser.cpp b/executor/xeTestResultParser.cpp
new file mode 100644
index 0000000..1c7072a
--- /dev/null
+++ b/executor/xeTestResultParser.cpp
@@ -0,0 +1,900 @@
+/*-------------------------------------------------------------------------
+ * 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 deInt64 toInt64 (const char* str)
+{
+ std::istringstream s (str);
+ deInt64 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
+{
+ deUint32 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 }
+};
+
+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 },
+ { 0x72863a54, "ShaderProgram", ri::TYPE_SHADERPROGRAM },
+ { 0xb4efc08d, "ShaderSource", ri::TYPE_SHADERSOURCE },
+ { 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 },
+};
+
+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 }
+};
+
+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)
+{
+ deUint32 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 deInt64 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 deUint8* 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("<VertexShader> 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_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_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++)
+ {
+ deUint8 byte = m_xmlParser.getDataByte(inNdx);
+ deUint8 decodedBits = 0;
+
+ if (de::inRange<deInt8>(byte, 'A', 'Z'))
+ decodedBits = byte - 'A';
+ else if (de::inRange<deInt8>(byte, 'a', 'z'))
+ decodedBits = ('Z'-'A'+1) + (byte-'a');
+ else if (de::inRange<deInt8>(byte, '0', '9'))
+ decodedBits = ('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");
+ deUint8* outPtr = &image->data[(m_base64DecodeOffset>>2)*3];
+
+ switch (phase)
+ {
+ case 0: outPtr[0] |= decodedBits<<2; break;
+ case 1: outPtr[0] |= (decodedBits>>4); outPtr[1] |= ((decodedBits&0xF)<<4); break;
+ case 2: outPtr[1] |= (decodedBits>>2); outPtr[2] |= ((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);
+}
+
+} // xe