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