blob: 39deeb87334c9c6e903a767590c32d5ddd5729d0 [file] [log] [blame]
Jarkko Poyry3c827362014-09-02 11:48:52 +03001/*-------------------------------------------------------------------------
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
27namespace xe
28{
29
30enum
31{
32 CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE = 1024
33};
34
35static int getNextBufferSize (int curSize, int minNewSize)
36{
37 return de::max(curSize*2, 1<<deLog2Ceil32(minNewSize));
38}
39
40ContainerFormatParser::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
48ContainerFormatParser::~ContainerFormatParser (void)
49{
50}
51
52void 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
60void ContainerFormatParser::error (const std::string& what)
61{
62 throw ContainerParseError(what);
63}
64
Jarkko Pöyry1592f162015-05-29 14:40:11 -070065void ContainerFormatParser::feed (const deUint8* bytes, size_t numBytes)
Jarkko Poyry3c827362014-09-02 11:48:52 +030066{
67 // Grow buffer if necessary.
Jarkko Pöyry1592f162015-05-29 14:40:11 -070068 if (m_buf.getNumFree() < (int)numBytes)
69 m_buf.resize(getNextBufferSize(m_buf.getSize(), m_buf.getNumElements()+(int)numBytes));
Jarkko Poyry3c827362014-09-02 11:48:52 +030070
71 // Append to front.
Jarkko Pöyry1592f162015-05-29 14:40:11 -070072 m_buf.pushFront(bytes, (int)numBytes);
Jarkko Poyry3c827362014-09-02 11:48:52 +030073
74 // If we haven't parsed complete element, re-try after data feed.
75 if (m_element == CONTAINERELEMENT_INCOMPLETE)
76 advance();
77}
78
79const char* ContainerFormatParser::getSessionInfoAttribute (void) const
80{
81 DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
82 return m_attribute.c_str();
83}
84
85const char* ContainerFormatParser::getSessionInfoValue (void) const
86{
87 DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
88 return m_value.c_str();
89}
90
91const char* ContainerFormatParser::getTestCasePath (void) const
92{
93 DE_ASSERT(m_element == CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT);
94 return m_value.c_str();
95}
96
97const char* ContainerFormatParser::getTerminateReason (void) const
98{
99 DE_ASSERT(m_element == CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT);
100 return m_value.c_str();
101}
102
103int ContainerFormatParser::getDataSize (void) const
104{
105 DE_ASSERT(m_element == CONTAINERELEMENT_TEST_LOG_DATA);
106 return m_elementLen;
107}
108
109void 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
117int 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
127void 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
188void 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
Pyry Haulos7704f192016-09-26 15:28:57 -0700268 // \note Candy produces unescaped timestamps.
Jarkko Poyry3c827362014-09-02 11:48:52 +0300269 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
293void 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