You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
8.0 KiB
327 lines
8.0 KiB
/*-------------------------------------------------------------------------
|
|
* 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 log container format parser.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "xeContainerFormatParser.hpp"
|
|
#include "deInt32.h"
|
|
|
|
namespace xe
|
|
{
|
|
|
|
enum
|
|
{
|
|
CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE = 1024
|
|
};
|
|
|
|
static int getNextBufferSize (int curSize, int minNewSize)
|
|
{
|
|
return de::max(curSize*2, 1<<deLog2Ceil32(minNewSize));
|
|
}
|
|
|
|
ContainerFormatParser::ContainerFormatParser (void)
|
|
: m_element (CONTAINERELEMENT_INCOMPLETE)
|
|
, m_elementLen (0)
|
|
, m_state (STATE_AT_LINE_START)
|
|
, m_buf (CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE)
|
|
{
|
|
}
|
|
|
|
ContainerFormatParser::~ContainerFormatParser (void)
|
|
{
|
|
}
|
|
|
|
void ContainerFormatParser::clear (void)
|
|
{
|
|
m_element = CONTAINERELEMENT_INCOMPLETE;
|
|
m_elementLen = 0;
|
|
m_state = STATE_AT_LINE_START;
|
|
m_buf.clear();
|
|
}
|
|
|
|
void ContainerFormatParser::error (const std::string& what)
|
|
{
|
|
throw ContainerParseError(what);
|
|
}
|
|
|
|
void ContainerFormatParser::feed (const deUint8* bytes, size_t numBytes)
|
|
{
|
|
// Grow buffer if necessary.
|
|
if (m_buf.getNumFree() < (int)numBytes)
|
|
m_buf.resize(getNextBufferSize(m_buf.getSize(), m_buf.getNumElements()+(int)numBytes));
|
|
|
|
// Append to front.
|
|
m_buf.pushFront(bytes, (int)numBytes);
|
|
|
|
// If we haven't parsed complete element, re-try after data feed.
|
|
if (m_element == CONTAINERELEMENT_INCOMPLETE)
|
|
advance();
|
|
}
|
|
|
|
const char* ContainerFormatParser::getSessionInfoAttribute (void) const
|
|
{
|
|
DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
|
|
return m_attribute.c_str();
|
|
}
|
|
|
|
const char* ContainerFormatParser::getSessionInfoValue (void) const
|
|
{
|
|
DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
|
|
return m_value.c_str();
|
|
}
|
|
|
|
const char* ContainerFormatParser::getTestCasePath (void) const
|
|
{
|
|
DE_ASSERT(m_element == CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT);
|
|
return m_value.c_str();
|
|
}
|
|
|
|
const char* ContainerFormatParser::getTerminateReason (void) const
|
|
{
|
|
DE_ASSERT(m_element == CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT);
|
|
return m_value.c_str();
|
|
}
|
|
|
|
int ContainerFormatParser::getDataSize (void) const
|
|
{
|
|
DE_ASSERT(m_element == CONTAINERELEMENT_TEST_LOG_DATA);
|
|
return m_elementLen;
|
|
}
|
|
|
|
void ContainerFormatParser::getData (deUint8* dst, int numBytes, int offset)
|
|
{
|
|
DE_ASSERT(de::inBounds(offset, 0, m_elementLen) && numBytes > 0 && de::inRange(numBytes+offset, 0, m_elementLen));
|
|
|
|
for (int ndx = 0; ndx < numBytes; ndx++)
|
|
dst[ndx] = m_buf.peekBack(offset+ndx);
|
|
}
|
|
|
|
int ContainerFormatParser::getChar (int offset) const
|
|
{
|
|
DE_ASSERT(de::inRange(offset, 0, m_buf.getNumElements()));
|
|
|
|
if (offset < m_buf.getNumElements())
|
|
return m_buf.peekBack(offset);
|
|
else
|
|
return END_OF_BUFFER;
|
|
}
|
|
|
|
void ContainerFormatParser::advance (void)
|
|
{
|
|
if (m_element != CONTAINERELEMENT_INCOMPLETE)
|
|
{
|
|
m_buf.popBack(m_elementLen);
|
|
|
|
m_element = CONTAINERELEMENT_INCOMPLETE;
|
|
m_elementLen = 0;
|
|
m_attribute.clear();
|
|
m_value.clear();
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
int curChar = getChar(m_elementLen);
|
|
|
|
if (curChar != (int)END_OF_BUFFER)
|
|
m_elementLen += 1;
|
|
|
|
if (curChar == END_OF_STRING)
|
|
{
|
|
if (m_elementLen == 1)
|
|
m_element = CONTAINERELEMENT_END_OF_STRING;
|
|
else if (m_state == STATE_CONTAINER_LINE)
|
|
parseContainerLine();
|
|
else
|
|
m_element = CONTAINERELEMENT_TEST_LOG_DATA;
|
|
|
|
break;
|
|
}
|
|
else if (curChar == (int)END_OF_BUFFER)
|
|
{
|
|
if (m_elementLen > 0 && m_state == STATE_DATA)
|
|
m_element = CONTAINERELEMENT_TEST_LOG_DATA;
|
|
|
|
break;
|
|
}
|
|
else if (curChar == '\r' || curChar == '\n')
|
|
{
|
|
// Check for \r\n
|
|
int nextChar = getChar(m_elementLen);
|
|
if (curChar == '\n' || (nextChar != (int)END_OF_BUFFER && nextChar != '\n'))
|
|
{
|
|
if (m_state == STATE_CONTAINER_LINE)
|
|
parseContainerLine();
|
|
else
|
|
m_element = CONTAINERELEMENT_TEST_LOG_DATA;
|
|
|
|
m_state = STATE_AT_LINE_START;
|
|
break;
|
|
}
|
|
// else handle following end or \n in next iteration.
|
|
}
|
|
else if (m_state == STATE_AT_LINE_START)
|
|
{
|
|
DE_ASSERT(m_elementLen == 1);
|
|
m_state = (curChar == '#') ? STATE_CONTAINER_LINE : STATE_DATA;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContainerFormatParser::parseContainerLine (void)
|
|
{
|
|
static const struct
|
|
{
|
|
const char* name;
|
|
ContainerElement element;
|
|
} s_elements[] =
|
|
{
|
|
{ "beginTestCaseResult", CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT },
|
|
{ "endTestCaseResult", CONTAINERELEMENT_END_TEST_CASE_RESULT },
|
|
{ "terminateTestCaseResult", CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT },
|
|
{ "sessionInfo", CONTAINERELEMENT_SESSION_INFO },
|
|
{ "beginSession", CONTAINERELEMENT_BEGIN_SESSION },
|
|
{ "endSession", CONTAINERELEMENT_END_SESSION }
|
|
};
|
|
|
|
DE_ASSERT(m_elementLen >= 1);
|
|
DE_ASSERT(getChar(0) == '#');
|
|
|
|
int offset = 1;
|
|
|
|
for (int elemNdx = 0; elemNdx < DE_LENGTH_OF_ARRAY(s_elements); elemNdx++)
|
|
{
|
|
bool isMatch = false;
|
|
int ndx = 0;
|
|
|
|
for (;;)
|
|
{
|
|
int bufChar = (offset+ndx < m_elementLen) ? getChar(offset+ndx) : 0;
|
|
bool bufEnd = bufChar == 0 || bufChar == ' ' || bufChar == '\r' || bufChar == '\n' || bufChar == (int)END_OF_BUFFER;
|
|
int elemChar = s_elements[elemNdx].name[ndx];
|
|
bool elemEnd = elemChar == 0;
|
|
|
|
if (bufEnd || elemEnd)
|
|
{
|
|
isMatch = bufEnd == elemEnd;
|
|
break;
|
|
}
|
|
else if (bufChar != elemChar)
|
|
break;
|
|
|
|
ndx += 1;
|
|
}
|
|
|
|
if (isMatch)
|
|
{
|
|
m_element = s_elements[elemNdx].element;
|
|
offset += ndx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (m_element)
|
|
{
|
|
case CONTAINERELEMENT_BEGIN_SESSION:
|
|
case CONTAINERELEMENT_END_SESSION:
|
|
case CONTAINERELEMENT_END_TEST_CASE_RESULT:
|
|
break; // No attribute or value.
|
|
|
|
case CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT:
|
|
case CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT:
|
|
if (getChar(offset) != ' ')
|
|
error("Expected value after instruction");
|
|
offset += 1;
|
|
parseContainerValue(m_value, offset);
|
|
break;
|
|
|
|
case CONTAINERELEMENT_SESSION_INFO:
|
|
if (getChar(offset) != ' ')
|
|
error("Expected attribute name after #sessionInfo");
|
|
offset += 1;
|
|
parseContainerValue(m_attribute, offset);
|
|
if (getChar(offset) != ' ')
|
|
error("No value for #sessionInfo attribute");
|
|
offset += 1;
|
|
|
|
if (m_attribute == "timestamp")
|
|
{
|
|
m_value.clear();
|
|
|
|
// \note Candy produces unescaped timestamps.
|
|
for (;;)
|
|
{
|
|
const int curChar = offset < m_elementLen ? getChar(offset) : 0;
|
|
const bool isEnd = curChar == 0 || curChar == (int)END_OF_BUFFER || curChar == '\n' || curChar == '\t';
|
|
|
|
if (isEnd)
|
|
break;
|
|
else
|
|
m_value.push_back((char)curChar);
|
|
|
|
offset += 1;
|
|
}
|
|
}
|
|
else
|
|
parseContainerValue(m_value, offset);
|
|
break;
|
|
|
|
default:
|
|
// \todo [2012-06-09 pyry] Implement better way to handle # at the beginning of log lines.
|
|
m_element = CONTAINERELEMENT_TEST_LOG_DATA;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ContainerFormatParser::parseContainerValue (std::string& dst, int& offset) const
|
|
{
|
|
DE_ASSERT(offset < m_elementLen);
|
|
|
|
bool isString = getChar(offset) == '"' || getChar(offset) == '\'';
|
|
int quotChar = isString ? getChar(offset) : 0;
|
|
|
|
if (isString)
|
|
offset += 1;
|
|
|
|
dst.clear();
|
|
|
|
for (;;)
|
|
{
|
|
int curChar = offset < m_elementLen ? getChar(offset) : 0;
|
|
bool isEnd = curChar == 0 || curChar == (int)END_OF_BUFFER ||
|
|
(isString ? (curChar == quotChar) : (curChar == ' ' || curChar == '\n' || curChar == '\r'));
|
|
|
|
if (isEnd)
|
|
break;
|
|
else
|
|
{
|
|
// \todo [2012-06-09 pyry] Escapes.
|
|
dst.push_back((char)curChar);
|
|
}
|
|
|
|
offset += 1;
|
|
}
|
|
|
|
if (isString && getChar(offset) == quotChar)
|
|
offset += 1;
|
|
}
|
|
|
|
} // xe
|