• 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 & 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