1 /*-------------------------------------------------------------------------
2 * Vulkan CTS Framework
3 * --------------------
4 *
5 * Copyright (c) 2020 The Khronos Group Inc.
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 Waiver mechanism implementation.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuWaiverUtil.hpp"
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28 #include <iomanip>
29 #include "deString.h"
30 #include "deStringUtil.hpp"
31 #include "xeXMLParser.hpp"
32 #include "tcuCommandLine.hpp"
33
34 namespace tcu
35 {
36
SessionInfo(uint32_t vendorId,uint32_t deviceId,const std::string & deviceName,const std::string & cmdLine)37 SessionInfo::SessionInfo(uint32_t vendorId, uint32_t deviceId, const std::string &deviceName,
38 const std::string &cmdLine)
39 : m_cmdLine(cmdLine)
40 {
41 m_info << std::hex << "#sessionInfo vendorID 0x" << vendorId << "\n"
42 << "#sessionInfo deviceID 0x" << deviceId << "\n"
43 << "#sessionInfo deviceName " << deviceName << "\n";
44 }
45
SessionInfo(std::string vendor,std::string renderer,const std::string & cmdLine)46 SessionInfo::SessionInfo(std::string vendor, std::string renderer, const std::string &cmdLine) : m_cmdLine(cmdLine)
47 {
48 m_info << "#sessionInfo vendor \"" << vendor << "\"\n"
49 << "#sessionInfo renderer \"" << renderer << "\"\n";
50 }
51
get()52 std::string SessionInfo::get()
53 {
54 if (!m_waiverUrls.empty())
55 {
56 m_info << "#sessionInfo waiverUrls \"" << m_waiverUrls << "\"\n";
57 m_waiverUrls.clear();
58 }
59 if (!m_cmdLine.empty())
60 {
61 m_info << "#sessionInfo commandLineParameters \"" << m_cmdLine << "\"\n";
62 m_cmdLine.clear();
63 }
64 return m_info.str();
65 }
66
67 // Base class for GL and VK waiver tree builders
68 class WaiverTreeBuilder
69 {
70 public:
71 typedef WaiverUtil::WaiverComponent WaiverComponent;
72
73 public:
74 WaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName, const char *vendorTag,
75 const char *deviceTag, SessionInfo &sessionInfo, std::vector<WaiverComponent> &waiverTree);
76
77 virtual ~WaiverTreeBuilder();
78
79 void build(void);
80
81 protected:
82 // structure representing component during tree construction
83 struct BuilComponent
84 {
85 std::string name;
86 uint32_t parentIndex; // index in allComponents vector
87 std::vector<uint32_t> childrenIndex; // index in allComponents vector
88
BuilComponenttcu::WaiverTreeBuilder::BuilComponent89 BuilComponent(std::string n, uint32_t p) : name(std::move(n)), parentIndex(p)
90 {
91 }
92 };
93
94 // parse waiver.xml and read list of waived tests defined
95 // specificly for current device id and current vendor id
96 void readWaivedTestsFromXML(void);
97
98 // use list of paths to build a temporary tree which
99 // consists of BuilComponents that help with tree construction
100 void buildTreeFromPathList(void);
101
102 // use temporary tree to create final tree containing
103 // only things that are needed during searches
104 void constructFinalTree(void);
105
106 // helper methods used to identify if proper waiver for vendor was found
107 virtual bool matchVendor(const std::string &vendor) const = 0;
108
109 // helper methods used after waiver for current vendor was found to check
110 // if it is defined also for currend deviceId/renderer
111 virtual bool matchDevice(const std::string &device) const = 0;
112
113 // helper method used in buildTreeFromPathList; returns index
114 // of component having same ancestors as the component specified
115 // in the argument or 0 when build tree does not include this component
116 uint32_t findComponentInBuildTree(const std::vector<std::string> &pathComponents, uint32_t index) const;
117
118 private:
119 const std::string &m_waiverFile;
120 const std::string &m_packageName;
121
122 const char *m_vendorTag;
123 const char *m_deviceTag;
124
125 // helper attributes used during construction
126 std::vector<std::string> m_testList;
127 std::vector<BuilComponent> m_buildTree;
128
129 // reference to object containing information about used waivers
130 SessionInfo &m_sessionInfo;
131
132 // reference to vector containing final tree
133 std::vector<WaiverComponent> &m_finalTree;
134 };
135
WaiverTreeBuilder(const std::string & waiverFile,const std::string & packageName,const char * vendorTag,const char * deviceTag,SessionInfo & sessionInfo,std::vector<WaiverComponent> & waiverTree)136 WaiverTreeBuilder::WaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName,
137 const char *vendorTag, const char *deviceTag, SessionInfo &sessionInfo,
138 std::vector<WaiverComponent> &waiverTree)
139 : m_waiverFile(waiverFile)
140 , m_packageName(packageName)
141 , m_vendorTag(vendorTag)
142 , m_deviceTag(deviceTag)
143 , m_sessionInfo(sessionInfo)
144 , m_finalTree(waiverTree)
145 {
146 }
147
~WaiverTreeBuilder()148 WaiverTreeBuilder::~WaiverTreeBuilder()
149 {
150 }
151
build(void)152 void WaiverTreeBuilder::build(void)
153 {
154 readWaivedTestsFromXML();
155 buildTreeFromPathList();
156 constructFinalTree();
157 }
158
readWaivedTestsFromXML()159 void WaiverTreeBuilder::readWaivedTestsFromXML()
160 {
161 std::ifstream iStream(m_waiverFile);
162 if (!iStream.is_open())
163 return;
164
165 // get whole waiver file content
166 std::stringstream buffer;
167 buffer << iStream.rdbuf();
168 std::string wholeContent = buffer.str();
169
170 // feed parser with xml content
171 xe::xml::Parser xmlParser;
172 xmlParser.feed(reinterpret_cast<const uint8_t *>(wholeContent.c_str()), static_cast<int>(wholeContent.size()));
173 xmlParser.advance();
174
175 // first we find matching vendor, then search for matching device/renderer and then memorize cases
176 bool vendorFound = false;
177 bool deviceFound = false;
178 bool scanDevice = false;
179 bool memorizeCase = false;
180 std::string waiverUrl;
181 std::vector<std::string> waiverTestList;
182
183 while (true)
184 {
185 // we are grabing elements one by one - depth-first traversal in pre-order
186 xe::xml::Element currElement = xmlParser.getElement();
187
188 // stop if there is parsing error or we didnt found
189 // waiver for current vendor id and device id/renderer
190 if (currElement == xe::xml::ELEMENT_INCOMPLETE || currElement == xe::xml::ELEMENT_END_OF_STRING)
191 break;
192
193 const char *elemName = xmlParser.getElementName();
194 switch (currElement)
195 {
196 case xe::xml::ELEMENT_START:
197 if (vendorFound)
198 {
199 if (!deviceFound)
200 {
201 // if we found proper vendor and are reading deviceIds/rendererers list then allow it
202 scanDevice = deStringEqual(elemName, m_deviceTag); // e.g. "d"
203 if (scanDevice)
204 break;
205 }
206
207 // if we found waiver for current vendor and are reading test case names then allow it
208 memorizeCase = deStringEqual(elemName, "t");
209 break;
210 }
211
212 // we are searching for waiver definition for current vendor, till we find
213 // it we skip everythingh; we also skip tags that we don't need eg. description
214 if (!deStringEqual(elemName, "waiver"))
215 break;
216
217 // we found waiver tag, check if it is deffined for current vendor
218 waiverTestList.clear();
219 if (xmlParser.hasAttribute(m_vendorTag))
220 {
221 vendorFound = matchVendor(xmlParser.getAttribute(m_vendorTag));
222 // if waiver vendor matches current one then memorize waiver url
223 // it will be needed when deviceId/renderer will match current one
224 if (vendorFound)
225 waiverUrl = xmlParser.getAttribute("url");
226 }
227 break;
228
229 case xe::xml::ELEMENT_DATA:
230 if (scanDevice)
231 {
232 // check if device read from xml matches current device/renderer
233 std::string waivedDevice;
234 xmlParser.getDataStr(waivedDevice);
235 deviceFound = matchDevice(waivedDevice);
236 }
237 else if (memorizeCase)
238 {
239 // memorize whats betwean <t></t> tags when case name starts with current package name
240 // note: waiver tree is constructed per package
241 std::string waivedCaseName;
242 xmlParser.getDataStr(waivedCaseName);
243 if (waivedCaseName.find(m_packageName) == 0)
244 waiverTestList.push_back(waivedCaseName);
245 }
246 break;
247
248 case xe::xml::ELEMENT_END:
249 memorizeCase = false;
250 scanDevice = false;
251 if (deStringEqual(elemName, "waiver"))
252 {
253 // when we found proper waiver we can copy memorized cases and update waiver info
254 if (vendorFound && deviceFound)
255 {
256 DE_ASSERT(m_testList.empty() || waiverUrl.empty());
257
258 std::string &urls = m_sessionInfo.m_waiverUrls;
259 m_testList.insert(m_testList.end(), waiverTestList.begin(), waiverTestList.end());
260
261 // if m_waiverUrls is not empty then we found another waiver
262 // definition that should be applyed for this device; we need to
263 // add space to urls attribute to separate new url from previous one
264 if (!urls.empty())
265 urls.append(" ");
266 urls.append(waiverUrl);
267 }
268 vendorFound = false;
269 deviceFound = false;
270 }
271 break;
272
273 default:
274 DE_ASSERT(false);
275 }
276
277 xmlParser.advance();
278 }
279 }
280
findComponentInBuildTree(const std::vector<std::string> & pathComponents,uint32_t index) const281 uint32_t WaiverTreeBuilder::findComponentInBuildTree(const std::vector<std::string> &pathComponents,
282 uint32_t index) const
283 {
284 const std::string &checkedName = pathComponents[index];
285
286 // check if same component is already in the build tree; we start from 1 - skiping root
287 for (uint32_t componentIndex = 1; componentIndex < m_buildTree.size(); ++componentIndex)
288 {
289 const BuilComponent &componentInTree = m_buildTree[componentIndex];
290 if (componentInTree.name != checkedName)
291 continue;
292
293 // names match so we need to make sure that all their ancestors match too;
294 uint32_t reverseLevel = index;
295 uint32_t ancestorInTreeIndex = componentInTree.parentIndex;
296
297 // if this component is the next after root then there is no ancestors to check
298 if (reverseLevel == 1)
299 return componentIndex;
300
301 while (--reverseLevel > 0)
302 {
303 // names dont match - we can move to searching other build tree items
304 if (pathComponents[reverseLevel] != m_buildTree[ancestorInTreeIndex].name)
305 break;
306
307 // when previous path component matches ancestor name then we need do check earlier path component
308 ancestorInTreeIndex = m_buildTree[ancestorInTreeIndex].parentIndex;
309
310 // we reached root
311 if (ancestorInTreeIndex == 0)
312 {
313 // if next level would be root then ancestors match
314 if (reverseLevel == 1)
315 return componentIndex;
316 // if next level is not root then ancestors dont match
317 break;
318 }
319 }
320 }
321
322 // searched path component is not in the tree
323 return 0;
324 }
325
buildTreeFromPathList(void)326 void WaiverTreeBuilder::buildTreeFromPathList(void)
327 {
328 if (m_testList.empty())
329 return;
330
331 uint32_t parentIndex = 0;
332
333 // construct root node
334 m_buildTree.emplace_back("root", 0);
335
336 for (const auto &path : m_testList)
337 {
338 const std::vector<std::string> pathComponents = de::splitString(path, '.');
339
340 // first component is parented to root
341 parentIndex = 0;
342
343 // iterate over all components of current path, but skip first one (e.g. "dEQP-VK", "KHR-GLES31")
344 for (uint32_t level = 1; level < pathComponents.size(); ++level)
345 {
346 // check if same component is already in the tree and we dont need to add it
347 uint32_t componentIndex = findComponentInBuildTree(pathComponents, level);
348 if (componentIndex)
349 {
350 parentIndex = componentIndex;
351 continue;
352 }
353
354 // component is not in the tree, add it
355 const std::string componentName = pathComponents[level];
356 m_buildTree.emplace_back(componentName, parentIndex);
357
358 // add current component as a child to its parent and assume
359 // that this component will be parent of next component
360 componentIndex = static_cast<uint32_t>(m_buildTree.size() - 1);
361 m_buildTree[parentIndex].childrenIndex.push_back(componentIndex);
362 parentIndex = componentIndex;
363 }
364 }
365 }
366
constructFinalTree(void)367 void WaiverTreeBuilder::constructFinalTree(void)
368 {
369 if (m_buildTree.empty())
370 return;
371
372 // translate vector of BuilComponents to vector of WaiverComponents
373 m_finalTree.resize(m_buildTree.size());
374 for (uint32_t i = 0; i < m_finalTree.size(); ++i)
375 {
376 BuilComponent &buildCmponent = m_buildTree[i];
377 WaiverComponent &waiverComponent = m_finalTree[i];
378
379 waiverComponent.name = std::move(buildCmponent.name);
380 waiverComponent.children.resize(buildCmponent.childrenIndex.size());
381
382 // set pointers for children
383 for (uint32_t j = 0; j < buildCmponent.childrenIndex.size(); ++j)
384 {
385 uint32_t childIndexInTree = buildCmponent.childrenIndex[j];
386 waiverComponent.children[j] = &m_finalTree[childIndexInTree];
387 }
388 }
389 }
390
391 // Class that builds a tree out of waiver definitions for OpenGL tests.
392 // Most of functionalities are shared betwean VK and GL builders and they
393 // were extracted to WaiverTreeBuilder base class.
394 class GLWaiverTreeBuilder : public WaiverTreeBuilder
395 {
396 public:
397 GLWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName, const std::string ¤tVendor,
398 const std::string ¤tRenderer, SessionInfo &sessionInfo,
399 std::vector<WaiverComponent> &waiverTree);
400
401 bool matchVendor(const std::string &vendor) const override;
402 bool matchDevice(const std::string &device) const override;
403
404 private:
405 const std::string m_currentVendor;
406 const std::string m_currentRenderer;
407 };
408
GLWaiverTreeBuilder(const std::string & waiverFile,const std::string & packageName,const std::string & currentVendor,const std::string & currentRenderer,SessionInfo & sessionInfo,std::vector<WaiverComponent> & waiverTree)409 GLWaiverTreeBuilder::GLWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName,
410 const std::string ¤tVendor, const std::string ¤tRenderer,
411 SessionInfo &sessionInfo, std::vector<WaiverComponent> &waiverTree)
412 : WaiverTreeBuilder(waiverFile, packageName, "vendor", "r", sessionInfo, waiverTree)
413 , m_currentVendor(currentVendor)
414 , m_currentRenderer(currentRenderer)
415 {
416 }
417
matchVendor(const std::string & vendor) const418 bool GLWaiverTreeBuilder::matchVendor(const std::string &vendor) const
419 {
420 return tcu::matchWildcards(vendor.cbegin(), vendor.cend(), m_currentVendor.cbegin(), m_currentVendor.cend(), false);
421 }
422
matchDevice(const std::string & device) const423 bool GLWaiverTreeBuilder::matchDevice(const std::string &device) const
424 {
425 // make sure that renderer name in .xml is not within "", those extra characters should be removed
426 DE_ASSERT(device[0] != '\"');
427
428 return tcu::matchWildcards(device.cbegin(), device.cend(), m_currentRenderer.cbegin(), m_currentRenderer.cend(),
429 false);
430 }
431
432 // Class that builds a tree out of waiver definitions for Vulkan tests.
433 // Most of functionalities are shared betwean VK and GL builders and they
434 // were extracted to WaiverTreeBuilder base class.
435 class VKWaiverTreeBuilder : public WaiverTreeBuilder
436 {
437 public:
438 VKWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName, const uint32_t currentVendor,
439 const uint32_t currentRenderer, SessionInfo &sessionInfo,
440 std::vector<WaiverComponent> &waiverTree);
441
442 bool matchVendor(const std::string &vendor) const override;
443 bool matchDevice(const std::string &device) const override;
444
445 private:
446 const uint32_t m_currentVendorId;
447 const uint32_t m_currentDeviceId;
448 };
449
VKWaiverTreeBuilder(const std::string & waiverFile,const std::string & packageName,const uint32_t currentVendor,const uint32_t currentRenderer,SessionInfo & sessionInfo,std::vector<WaiverComponent> & waiverTree)450 VKWaiverTreeBuilder::VKWaiverTreeBuilder(const std::string &waiverFile, const std::string &packageName,
451 const uint32_t currentVendor, const uint32_t currentRenderer,
452 SessionInfo &sessionInfo, std::vector<WaiverComponent> &waiverTree)
453 : WaiverTreeBuilder(waiverFile, packageName, "vendorId", "d", sessionInfo, waiverTree)
454 , m_currentVendorId(currentVendor)
455 , m_currentDeviceId(currentRenderer)
456 {
457 }
458
matchVendor(const std::string & vendor) const459 bool VKWaiverTreeBuilder::matchVendor(const std::string &vendor) const
460 {
461 return (m_currentVendorId == static_cast<uint32_t>(std::stoul(vendor, 0, 0)));
462 }
463
matchDevice(const std::string & device) const464 bool VKWaiverTreeBuilder::matchDevice(const std::string &device) const
465 {
466 return (m_currentDeviceId == static_cast<uint32_t>(std::stoul(device, 0, 0)));
467 }
468
setup(const std::string waiverFile,std::string packageName,uint32_t vendorId,uint32_t deviceId,SessionInfo & sessionInfo)469 void WaiverUtil::setup(const std::string waiverFile, std::string packageName, uint32_t vendorId, uint32_t deviceId,
470 SessionInfo &sessionInfo)
471 {
472 VKWaiverTreeBuilder(waiverFile, packageName, vendorId, deviceId, sessionInfo, m_waiverTree).build();
473 }
474
setup(const std::string waiverFile,std::string packageName,std::string vendor,std::string renderer,SessionInfo & sessionInfo)475 void WaiverUtil::setup(const std::string waiverFile, std::string packageName, std::string vendor, std::string renderer,
476 SessionInfo &sessionInfo)
477 {
478 GLWaiverTreeBuilder(waiverFile, packageName, vendor, renderer, sessionInfo, m_waiverTree).build();
479 }
480
isOnWaiverList(const std::string & casePath) const481 bool WaiverUtil::isOnWaiverList(const std::string &casePath) const
482 {
483 if (m_waiverTree.empty())
484 return false;
485
486 // skip root e.g. "dEQP-VK"
487 size_t firstDotPos = casePath.find('.');
488 std::string::const_iterator componentStart = casePath.cbegin() + firstDotPos + 1;
489 std::string::const_iterator componentEnd = componentStart;
490 std::string::const_iterator pathEnd = casePath.cend();
491 const WaiverComponent *waiverComponent = m_waiverTree.data();
492
493 // check path component by component
494 while (true)
495 {
496 // find the last character of next component
497 ++componentEnd;
498 for (; componentEnd < pathEnd; ++componentEnd)
499 {
500 if (*componentEnd == '.')
501 break;
502 }
503
504 // check if one of children has the same component name
505 for (const auto &c : waiverComponent->children)
506 {
507 bool matchFound =
508 tcu::matchWildcards(c->name.cbegin(), c->name.cend(), componentStart, componentEnd, false);
509
510 // current waiver component matches curent path component - go to next component
511 if (matchFound)
512 {
513 waiverComponent = c;
514 break;
515 }
516 }
517
518 // we checked all components - if our pattern was a leaf then this test should be waived
519 if (componentEnd == pathEnd)
520 return waiverComponent->children.empty();
521
522 // go to next test path component
523 componentStart = componentEnd + 1;
524 }
525 return false;
526 }
527
528 } // namespace tcu
529