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