Home | History | Annotate | Download | only in executor
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Test Executor
      3  * ------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Test log container format parser.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xeContainerFormatParser.hpp"
     25 #include "deInt32.h"
     26 
     27 namespace xe
     28 {
     29 
     30 enum
     31 {
     32 	CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE = 1024
     33 };
     34 
     35 static int getNextBufferSize (int curSize, int minNewSize)
     36 {
     37 	return de::max(curSize*2, 1<<deLog2Ceil32(minNewSize));
     38 }
     39 
     40 ContainerFormatParser::ContainerFormatParser (void)
     41 	: m_element		(CONTAINERELEMENT_INCOMPLETE)
     42 	, m_elementLen	(0)
     43 	, m_state		(STATE_AT_LINE_START)
     44 	, m_buf			(CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE)
     45 {
     46 }
     47 
     48 ContainerFormatParser::~ContainerFormatParser (void)
     49 {
     50 }
     51 
     52 void ContainerFormatParser::clear (void)
     53 {
     54 	m_element		= CONTAINERELEMENT_INCOMPLETE;
     55 	m_elementLen	= 0;
     56 	m_state			= STATE_AT_LINE_START;
     57 	m_buf.clear();
     58 }
     59 
     60 void ContainerFormatParser::error (const std::string& what)
     61 {
     62 	throw ContainerParseError(what);
     63 }
     64 
     65 void ContainerFormatParser::feed (const deUint8* bytes, size_t numBytes)
     66 {
     67 	// Grow buffer if necessary.
     68 	if (m_buf.getNumFree() < (int)numBytes)
     69 		m_buf.resize(getNextBufferSize(m_buf.getSize(), m_buf.getNumElements()+(int)numBytes));
     70 
     71 	// Append to front.
     72 	m_buf.pushFront(bytes, (int)numBytes);
     73 
     74 	// If we haven't parsed complete element, re-try after data feed.
     75 	if (m_element == CONTAINERELEMENT_INCOMPLETE)
     76 		advance();
     77 }
     78 
     79 const char* ContainerFormatParser::getSessionInfoAttribute (void) const
     80 {
     81 	DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
     82 	return m_attribute.c_str();
     83 }
     84 
     85 const char* ContainerFormatParser::getSessionInfoValue (void) const
     86 {
     87 	DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
     88 	return m_value.c_str();
     89 }
     90 
     91 const char* ContainerFormatParser::getTestCasePath (void) const
     92 {
     93 	DE_ASSERT(m_element == CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT);
     94 	return m_value.c_str();
     95 }
     96 
     97 const char* ContainerFormatParser::getTerminateReason (void) const
     98 {
     99 	DE_ASSERT(m_element == CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT);
    100 	return m_value.c_str();
    101 }
    102 
    103 int ContainerFormatParser::getDataSize (void) const
    104 {
    105 	DE_ASSERT(m_element == CONTAINERELEMENT_TEST_LOG_DATA);
    106 	return m_elementLen;
    107 }
    108 
    109 void ContainerFormatParser::getData (deUint8* dst, int numBytes, int offset)
    110 {
    111 	DE_ASSERT(de::inBounds(offset, 0, m_elementLen) && numBytes > 0 && de::inRange(numBytes+offset, 0, m_elementLen));
    112 
    113 	for (int ndx = 0; ndx < numBytes; ndx++)
    114 		dst[ndx] = m_buf.peekBack(offset+ndx);
    115 }
    116 
    117 int ContainerFormatParser::getChar (int offset) const
    118 {
    119 	DE_ASSERT(de::inRange(offset, 0, m_buf.getNumElements()));
    120 
    121 	if (offset < m_buf.getNumElements())
    122 		return m_buf.peekBack(offset);
    123 	else
    124 		return END_OF_BUFFER;
    125 }
    126 
    127 void ContainerFormatParser::advance (void)
    128 {
    129 	if (m_element != CONTAINERELEMENT_INCOMPLETE)
    130 	{
    131 		m_buf.popBack(m_elementLen);
    132 
    133 		m_element		= CONTAINERELEMENT_INCOMPLETE;
    134 		m_elementLen	= 0;
    135 		m_attribute.clear();
    136 		m_value.clear();
    137 	}
    138 
    139 	for (;;)
    140 	{
    141 		int curChar = getChar(m_elementLen);
    142 
    143 		if (curChar != (int)END_OF_BUFFER)
    144 			m_elementLen += 1;
    145 
    146 		if (curChar == END_OF_STRING)
    147 		{
    148 			if (m_elementLen == 1)
    149 				m_element = CONTAINERELEMENT_END_OF_STRING;
    150 			else if (m_state == STATE_CONTAINER_LINE)
    151 				parseContainerLine();
    152 			else
    153 				m_element = CONTAINERELEMENT_TEST_LOG_DATA;
    154 
    155 			break;
    156 		}
    157 		else if (curChar == (int)END_OF_BUFFER)
    158 		{
    159 			if (m_elementLen > 0 && m_state == STATE_DATA)
    160 				m_element = CONTAINERELEMENT_TEST_LOG_DATA;
    161 
    162 			break;
    163 		}
    164 		else if (curChar == '\r' || curChar == '\n')
    165 		{
    166 			// Check for \r\n
    167 			int nextChar = getChar(m_elementLen);
    168 			if (curChar == '\n' || (nextChar != (int)END_OF_BUFFER && nextChar != '\n'))
    169 			{
    170 				if (m_state == STATE_CONTAINER_LINE)
    171 					parseContainerLine();
    172 				else
    173 					m_element = CONTAINERELEMENT_TEST_LOG_DATA;
    174 
    175 				m_state = STATE_AT_LINE_START;
    176 				break;
    177 			}
    178 			// else handle following end or \n in next iteration.
    179 		}
    180 		else if (m_state == STATE_AT_LINE_START)
    181 		{
    182 			DE_ASSERT(m_elementLen == 1);
    183 			m_state = (curChar == '#') ? STATE_CONTAINER_LINE : STATE_DATA;
    184 		}
    185 	}
    186 }
    187 
    188 void ContainerFormatParser::parseContainerLine (void)
    189 {
    190 	static const struct
    191 	{
    192 		const char*			name;
    193 		ContainerElement	element;
    194 	} s_elements[] =
    195 	{
    196 		{ "beginTestCaseResult",		CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT		},
    197 		{ "endTestCaseResult",			CONTAINERELEMENT_END_TEST_CASE_RESULT		},
    198 		{ "terminateTestCaseResult",	CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT	},
    199 		{ "sessionInfo",				CONTAINERELEMENT_SESSION_INFO				},
    200 		{ "beginSession",				CONTAINERELEMENT_BEGIN_SESSION				},
    201 		{ "endSession",					CONTAINERELEMENT_END_SESSION				}
    202 	};
    203 
    204 	DE_ASSERT(m_elementLen >= 1);
    205 	DE_ASSERT(getChar(0) == '#');
    206 
    207 	int offset = 1;
    208 
    209 	for (int elemNdx = 0; elemNdx < DE_LENGTH_OF_ARRAY(s_elements); elemNdx++)
    210 	{
    211 		bool	isMatch	= false;
    212 		int		ndx		= 0;
    213 
    214 		for (;;)
    215 		{
    216 			int		bufChar		= (offset+ndx < m_elementLen) ? getChar(offset+ndx) : 0;
    217 			bool	bufEnd		= bufChar == 0 || bufChar == ' ' || bufChar == '\r' || bufChar == '\n' || bufChar == (int)END_OF_BUFFER;
    218 			int		elemChar	= s_elements[elemNdx].name[ndx];
    219 			bool	elemEnd		= elemChar == 0;
    220 
    221 			if (bufEnd || elemEnd)
    222 			{
    223 				isMatch = bufEnd == elemEnd;
    224 				break;
    225 			}
    226 			else if (bufChar != elemChar)
    227 				break;
    228 
    229 			ndx += 1;
    230 		}
    231 
    232 		if (isMatch)
    233 		{
    234 			m_element	 = s_elements[elemNdx].element;
    235 			offset		+= ndx;
    236 			break;
    237 		}
    238 	}
    239 
    240 	switch (m_element)
    241 	{
    242 		case CONTAINERELEMENT_BEGIN_SESSION:
    243 		case CONTAINERELEMENT_END_SESSION:
    244 		case CONTAINERELEMENT_END_TEST_CASE_RESULT:
    245 			break; // No attribute or value.
    246 
    247 		case CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT:
    248 		case CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT:
    249 			if (getChar(offset) != ' ')
    250 				error("Expected value after instruction");
    251 			offset += 1;
    252 			parseContainerValue(m_value, offset);
    253 			break;
    254 
    255 		case CONTAINERELEMENT_SESSION_INFO:
    256 			if (getChar(offset) != ' ')
    257 				error("Expected attribute name after #sessionInfo");
    258 			offset += 1;
    259 			parseContainerValue(m_attribute, offset);
    260 			if (getChar(offset) != ' ')
    261 				error("No value for #sessionInfo attribute");
    262 			offset += 1;
    263 
    264 			if (m_attribute == "timestamp")
    265 			{
    266 				m_value.clear();
    267 
    268 				// \note Candy produces unescaped timestamps.
    269 				for (;;)
    270 				{
    271 					const int	curChar	= offset < m_elementLen ? getChar(offset) : 0;
    272 					const bool	isEnd	= curChar == 0 || curChar == (int)END_OF_BUFFER || curChar == '\n' || curChar == '\t';
    273 
    274 					if (isEnd)
    275 						break;
    276 					else
    277 						m_value.push_back((char)curChar);
    278 
    279 					offset += 1;
    280 				}
    281 			}
    282 			else
    283 				parseContainerValue(m_value, offset);
    284 			break;
    285 
    286 		default:
    287 			// \todo [2012-06-09 pyry] Implement better way to handle # at the beginning of log lines.
    288 			m_element = CONTAINERELEMENT_TEST_LOG_DATA;
    289 			break;
    290 	}
    291 }
    292 
    293 void ContainerFormatParser::parseContainerValue (std::string& dst, int& offset) const
    294 {
    295 	DE_ASSERT(offset < m_elementLen);
    296 
    297 	bool	isString	= getChar(offset) == '"' || getChar(offset) == '\'';
    298 	int		quotChar	= isString ? getChar(offset) : 0;
    299 
    300 	if (isString)
    301 		offset += 1;
    302 
    303 	dst.clear();
    304 
    305 	for (;;)
    306 	{
    307 		int		curChar		= offset < m_elementLen ? getChar(offset) : 0;
    308 		bool	isEnd		= curChar == 0 || curChar == (int)END_OF_BUFFER ||
    309 							  (isString ? (curChar == quotChar) : (curChar == ' ' || curChar == '\n' || curChar == '\r'));
    310 
    311 		if (isEnd)
    312 			break;
    313 		else
    314 		{
    315 			// \todo [2012-06-09 pyry] Escapes.
    316 			dst.push_back((char)curChar);
    317 		}
    318 
    319 		offset += 1;
    320 	}
    321 
    322 	if (isString && getChar(offset) == quotChar)
    323 		offset += 1;
    324 }
    325 
    326 } // xe
    327