Merge "Don't require supported binary formats in negative tests." into marshmallow...
[platform/upstream/VK-GL-CTS.git] / executor / xeContainerFormatParser.cpp
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