• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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