You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
565 lines
17 KiB
565 lines
17 KiB
/*-------------------------------------------------------------------------
|
|
* Vulkan CTS Framework
|
|
* --------------------
|
|
*
|
|
* Copyright (c) 2020 The Khronos Group Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Waiver mechanism implementation.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "tcuWaiverUtil.hpp"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include "deString.h"
|
|
#include "deStringUtil.hpp"
|
|
#include "xeXMLParser.hpp"
|
|
#include "tcuCommandLine.hpp"
|
|
|
|
namespace tcu
|
|
{
|
|
|
|
SessionInfo::SessionInfo(deUint32 vendorId,
|
|
deUint32 deviceId,
|
|
const std::string& cmdLine)
|
|
: m_cmdLine (cmdLine)
|
|
{
|
|
m_info << std::hex
|
|
<< "#sessionInfo vendorID 0x" << vendorId << "\n"
|
|
<< "#sessionInfo deviceID 0x" << deviceId << "\n";
|
|
}
|
|
|
|
SessionInfo::SessionInfo(std::string vendor,
|
|
std::string renderer,
|
|
const std::string& cmdLine)
|
|
: m_cmdLine (cmdLine)
|
|
{
|
|
m_info << "#sessionInfo vendor \"" << vendor << "\"\n"
|
|
<< "#sessionInfo renderer \"" << renderer << "\"\n";
|
|
}
|
|
|
|
std::string SessionInfo::get()
|
|
{
|
|
if (!m_waiverUrls.empty())
|
|
{
|
|
m_info << "#sessionInfo waiverUrls \"" << m_waiverUrls << "\"\n";
|
|
m_waiverUrls.clear();
|
|
}
|
|
if (!m_cmdLine.empty())
|
|
{
|
|
m_info << "#sessionInfo commandLineParameters \"" << m_cmdLine << "\"\n";
|
|
m_cmdLine.clear();
|
|
}
|
|
return m_info.str();
|
|
}
|
|
|
|
// Base class for GL and VK waiver tree builders
|
|
class WaiverTreeBuilder
|
|
{
|
|
public:
|
|
|
|
typedef WaiverUtil::WaiverComponent WaiverComponent;
|
|
|
|
public:
|
|
WaiverTreeBuilder (const std::string& waiverFile,
|
|
const std::string& packageName,
|
|
const char* vendorTag,
|
|
const char* deviceTag,
|
|
SessionInfo& sessionInfo,
|
|
std::vector<WaiverComponent>& waiverTree);
|
|
|
|
virtual ~WaiverTreeBuilder();
|
|
|
|
void build (void);
|
|
|
|
protected:
|
|
|
|
// structure representing component during tree construction
|
|
struct BuilComponent
|
|
{
|
|
std::string name;
|
|
deUint32 parentIndex; // index in allComponents vector
|
|
std::vector<deUint32> childrenIndex; // index in allComponents vector
|
|
|
|
BuilComponent(std::string n, deUint32 p)
|
|
: name(std::move(n))
|
|
, parentIndex(p)
|
|
{}
|
|
};
|
|
|
|
// parse waiver.xml and read list of waived tests defined
|
|
// specificly for current device id and current vendor id
|
|
void readWaivedTestsFromXML (void);
|
|
|
|
// use list of paths to build a temporary tree which
|
|
// consists of BuilComponents that help with tree construction
|
|
void buildTreeFromPathList (void);
|
|
|
|
// use temporary tree to create final tree containing
|
|
// only things that are needed during searches
|
|
void constructFinalTree (void);
|
|
|
|
// helper methods used to identify if proper waiver for vendor was found
|
|
virtual bool matchVendor (const std::string& vendor) const = 0;
|
|
|
|
// helper methods used after waiver for current vendor was found to check
|
|
// if it is defined also for currend deviceId/renderer
|
|
virtual bool matchDevice (const std::string& device) const = 0;
|
|
|
|
// helper method used in buildTreeFromPathList; returns index
|
|
// of component having same ancestors as the component specified
|
|
// in the argument or 0 when build tree does not include this component
|
|
deUint32 findComponentInBuildTree(const std::vector<std::string>& pathComponents, deUint32 index) const;
|
|
|
|
private:
|
|
const std::string& m_waiverFile;
|
|
const std::string& m_packageName;
|
|
|
|
const char* m_vendorTag;
|
|
const char* m_deviceTag;
|
|
|
|
// helper attributes used during construction
|
|
std::vector<std::string> m_testList;
|
|
std::vector<BuilComponent> m_buildTree;
|
|
|
|
// reference to object containing information about used waivers
|
|
SessionInfo& m_sessionInfo;
|
|
|
|
// reference to vector containing final tree
|
|
std::vector<WaiverComponent>& m_finalTree;
|
|
};
|
|
|
|
WaiverTreeBuilder::WaiverTreeBuilder(const std::string& waiverFile,
|
|
const std::string& packageName,
|
|
const char* vendorTag,
|
|
const char* deviceTag,
|
|
SessionInfo& sessionInfo,
|
|
std::vector<WaiverComponent>& waiverTree)
|
|
: m_waiverFile (waiverFile)
|
|
, m_packageName (packageName)
|
|
, m_vendorTag (vendorTag)
|
|
, m_deviceTag (deviceTag)
|
|
, m_sessionInfo (sessionInfo)
|
|
, m_finalTree (waiverTree)
|
|
{
|
|
}
|
|
|
|
WaiverTreeBuilder::~WaiverTreeBuilder()
|
|
{
|
|
}
|
|
|
|
void WaiverTreeBuilder::build(void)
|
|
{
|
|
readWaivedTestsFromXML();
|
|
buildTreeFromPathList();
|
|
constructFinalTree();
|
|
}
|
|
|
|
void WaiverTreeBuilder::readWaivedTestsFromXML()
|
|
{
|
|
std::ifstream iStream(m_waiverFile);
|
|
if (!iStream.is_open())
|
|
return;
|
|
|
|
// get whole waiver file content
|
|
std::stringstream buffer;
|
|
buffer << iStream.rdbuf();
|
|
std::string wholeContent = buffer.str();
|
|
|
|
// feed parser with xml content
|
|
xe::xml::Parser xmlParser;
|
|
xmlParser.feed(reinterpret_cast<const deUint8*>(wholeContent.c_str()), static_cast<int>(wholeContent.size()));
|
|
xmlParser.advance();
|
|
|
|
// first we find matching vendor, then search for matching device/renderer and then memorize cases
|
|
bool vendorFound = false;
|
|
bool deviceFound = false;
|
|
bool scanDevice = false;
|
|
bool memorizeCase = false;
|
|
std::string waiverUrl;
|
|
std::vector<std::string> waiverTestList;
|
|
|
|
while (true)
|
|
{
|
|
// we are grabing elements one by one - depth-first traversal in pre-order
|
|
xe::xml::Element currElement = xmlParser.getElement();
|
|
|
|
// stop if there is parsing error or we didnt found
|
|
// waiver for current vendor id and device id/renderer
|
|
if (currElement == xe::xml::ELEMENT_INCOMPLETE ||
|
|
currElement == xe::xml::ELEMENT_END_OF_STRING)
|
|
break;
|
|
|
|
const char* elemName = xmlParser.getElementName();
|
|
switch (currElement)
|
|
{
|
|
case xe::xml::ELEMENT_START:
|
|
if (vendorFound)
|
|
{
|
|
if (!deviceFound)
|
|
{
|
|
// if we found proper vendor and are reading deviceIds/rendererers list then allow it
|
|
scanDevice = deStringEqual(elemName, m_deviceTag); // e.g. "d"
|
|
if (scanDevice)
|
|
break;
|
|
}
|
|
|
|
// if we found waiver for current vendor and are reading test case names then allow it
|
|
memorizeCase = deStringEqual(elemName, "t");
|
|
break;
|
|
}
|
|
|
|
// we are searching for waiver definition for current vendor, till we find
|
|
// it we skip everythingh; we also skip tags that we don't need eg. description
|
|
if (!deStringEqual(elemName, "waiver"))
|
|
break;
|
|
|
|
// we found waiver tag, check if it is deffined for current vendor
|
|
waiverTestList.clear();
|
|
if (xmlParser.hasAttribute(m_vendorTag))
|
|
{
|
|
vendorFound = matchVendor(xmlParser.getAttribute(m_vendorTag));
|
|
// if waiver vendor matches current one then memorize waiver url
|
|
// it will be needed when deviceId/renderer will match current one
|
|
if (vendorFound)
|
|
waiverUrl = xmlParser.getAttribute("url");
|
|
}
|
|
break;
|
|
|
|
case xe::xml::ELEMENT_DATA:
|
|
if (scanDevice)
|
|
{
|
|
// check if device read from xml matches current device/renderer
|
|
std::string waivedDevice;
|
|
xmlParser.getDataStr(waivedDevice);
|
|
deviceFound = matchDevice(waivedDevice);
|
|
}
|
|
else if (memorizeCase)
|
|
{
|
|
// memorize whats betwean <t></t> tags when case name starts with current package name
|
|
// note: waiver tree is constructed per package
|
|
std::string waivedCaseName;
|
|
xmlParser.getDataStr(waivedCaseName);
|
|
if (waivedCaseName.find(m_packageName) == 0)
|
|
waiverTestList.push_back(waivedCaseName);
|
|
}
|
|
break;
|
|
|
|
case xe::xml::ELEMENT_END:
|
|
memorizeCase = false;
|
|
scanDevice = false;
|
|
if (deStringEqual(elemName, "waiver"))
|
|
{
|
|
// when we found proper waiver we can copy memorized cases and update waiver info
|
|
if (vendorFound && deviceFound)
|
|
{
|
|
DE_ASSERT(m_testList.empty() || waiverUrl.empty());
|
|
|
|
std::string& urls = m_sessionInfo.m_waiverUrls;
|
|
m_testList.insert(m_testList.end(), waiverTestList.begin(), waiverTestList.end());
|
|
|
|
// if m_waiverUrls is not empty then we found another waiver
|
|
// definition that should be applyed for this device; we need to
|
|
// add space to urls attribute to separate new url from previous one
|
|
if (!urls.empty())
|
|
urls.append(" ");
|
|
urls.append(waiverUrl);
|
|
}
|
|
vendorFound = false;
|
|
deviceFound = false;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
}
|
|
|
|
xmlParser.advance();
|
|
}
|
|
}
|
|
|
|
deUint32 WaiverTreeBuilder::findComponentInBuildTree(const std::vector<std::string>& pathComponents, deUint32 index) const
|
|
{
|
|
const std::string& checkedName = pathComponents[index];
|
|
|
|
// check if same component is already in the build tree; we start from 1 - skiping root
|
|
for (deUint32 componentIndex = 1 ; componentIndex < m_buildTree.size() ; ++componentIndex)
|
|
{
|
|
const BuilComponent& componentInTree = m_buildTree[componentIndex];
|
|
if (componentInTree.name != checkedName)
|
|
continue;
|
|
|
|
// names match so we need to make sure that all their ancestors match too;
|
|
deUint32 reverseLevel = index;
|
|
deUint32 ancestorInTreeIndex = componentInTree.parentIndex;
|
|
|
|
// if this component is the next after root then there is no ancestors to check
|
|
if (reverseLevel == 1)
|
|
return componentIndex;
|
|
|
|
while (--reverseLevel > 0)
|
|
{
|
|
// names dont match - we can move to searching other build tree items
|
|
if (pathComponents[reverseLevel] != m_buildTree[ancestorInTreeIndex].name)
|
|
break;
|
|
|
|
// when previous path component matches ancestor name then we need do check earlier path component
|
|
ancestorInTreeIndex = m_buildTree[ancestorInTreeIndex].parentIndex;
|
|
|
|
// we reached root
|
|
if (ancestorInTreeIndex == 0)
|
|
{
|
|
// if next level would be root then ancestors match
|
|
if (reverseLevel == 1)
|
|
return componentIndex;
|
|
// if next level is not root then ancestors dont match
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// searched path component is not in the tree
|
|
return 0;
|
|
}
|
|
|
|
void WaiverTreeBuilder::buildTreeFromPathList(void)
|
|
{
|
|
if (m_testList.empty())
|
|
return;
|
|
|
|
deUint32 parentIndex = 0;
|
|
|
|
// construct root node
|
|
m_buildTree.emplace_back("root", DE_NULL);
|
|
|
|
for (const auto& path : m_testList)
|
|
{
|
|
const std::vector<std::string> pathComponents = de::splitString(path, '.');
|
|
|
|
// first component is parented to root
|
|
parentIndex = 0;
|
|
|
|
// iterate over all components of current path, but skip first one (e.g. "dEQP-VK", "KHR-GLES31")
|
|
for (deUint32 level = 1 ; level < pathComponents.size() ; ++level)
|
|
{
|
|
// check if same component is already in the tree and we dont need to add it
|
|
deUint32 componentIndex = findComponentInBuildTree(pathComponents, level);
|
|
if (componentIndex)
|
|
{
|
|
parentIndex = componentIndex;
|
|
continue;
|
|
}
|
|
|
|
// component is not in the tree, add it
|
|
const std::string componentName = pathComponents[level];
|
|
m_buildTree.emplace_back(componentName, parentIndex);
|
|
|
|
// add current component as a child to its parent and assume
|
|
// that this component will be parent of next component
|
|
componentIndex = static_cast<deUint32>(m_buildTree.size() - 1);
|
|
m_buildTree[parentIndex].childrenIndex.push_back(componentIndex);
|
|
parentIndex = componentIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WaiverTreeBuilder::constructFinalTree(void)
|
|
{
|
|
if (m_buildTree.empty())
|
|
return;
|
|
|
|
// translate vector of BuilComponents to vector of WaiverComponents
|
|
m_finalTree.resize(m_buildTree.size());
|
|
for (deUint32 i = 0; i < m_finalTree.size(); ++i)
|
|
{
|
|
BuilComponent& buildCmponent = m_buildTree[i];
|
|
WaiverComponent& waiverComponent = m_finalTree[i];
|
|
|
|
waiverComponent.name = std::move(buildCmponent.name);
|
|
waiverComponent.children.resize(buildCmponent.childrenIndex.size());
|
|
|
|
// set pointers for children
|
|
for (deUint32 j = 0; j < buildCmponent.childrenIndex.size(); ++j)
|
|
{
|
|
deUint32 childIndexInTree = buildCmponent.childrenIndex[j];
|
|
waiverComponent.children[j] = &m_finalTree[childIndexInTree];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Class that builds a tree out of waiver definitions for OpenGL tests.
|
|
// Most of functionalities are shared betwean VK and GL builders and they
|
|
// were extracted to WaiverTreeBuilder base class.
|
|
class GLWaiverTreeBuilder : public WaiverTreeBuilder
|
|
{
|
|
public:
|
|
GLWaiverTreeBuilder (const std::string& waiverFile,
|
|
const std::string& packageName,
|
|
const std::string& currentVendor,
|
|
const std::string& currentRenderer,
|
|
SessionInfo& sessionInfo,
|
|
std::vector<WaiverComponent>& waiverTree);
|
|
|
|
bool matchVendor (const std::string& vendor) const override;
|
|
bool matchDevice (const std::string& device) const override;
|
|
|
|
private:
|
|
|
|
const std::string m_currentVendor;
|
|
const std::string m_currentRenderer;
|
|
};
|
|
|
|
GLWaiverTreeBuilder::GLWaiverTreeBuilder(const std::string& waiverFile,
|
|
const std::string& packageName,
|
|
const std::string& currentVendor,
|
|
const std::string& currentRenderer,
|
|
SessionInfo& sessionInfo,
|
|
std::vector<WaiverComponent>& waiverTree)
|
|
: WaiverTreeBuilder (waiverFile, packageName, "vendor", "r", sessionInfo, waiverTree)
|
|
, m_currentVendor (currentVendor)
|
|
, m_currentRenderer (currentRenderer)
|
|
{
|
|
}
|
|
|
|
bool GLWaiverTreeBuilder::matchVendor(const std::string& vendor) const
|
|
{
|
|
return tcu::matchWildcards(vendor.cbegin(),
|
|
vendor.cend(),
|
|
m_currentVendor.cbegin(),
|
|
m_currentVendor.cend(),
|
|
false);
|
|
}
|
|
|
|
bool GLWaiverTreeBuilder::matchDevice(const std::string& device) const
|
|
{
|
|
// make sure that renderer name in .xml is not within "", those extra characters should be removed
|
|
DE_ASSERT(device[0] != '\"');
|
|
|
|
return tcu::matchWildcards(device.cbegin(),
|
|
device.cend(),
|
|
m_currentRenderer.cbegin(),
|
|
m_currentRenderer.cend(),
|
|
false);
|
|
}
|
|
|
|
// Class that builds a tree out of waiver definitions for Vulkan tests.
|
|
// Most of functionalities are shared betwean VK and GL builders and they
|
|
// were extracted to WaiverTreeBuilder base class.
|
|
class VKWaiverTreeBuilder : public WaiverTreeBuilder
|
|
{
|
|
public:
|
|
VKWaiverTreeBuilder (const std::string& waiverFile,
|
|
const std::string& packageName,
|
|
const deUint32 currentVendor,
|
|
const deUint32 currentRenderer,
|
|
SessionInfo& sessionInfo,
|
|
std::vector<WaiverComponent>& waiverTree);
|
|
|
|
bool matchVendor (const std::string& vendor) const override;
|
|
bool matchDevice (const std::string& device) const override;
|
|
|
|
private:
|
|
|
|
const deUint32 m_currentVendorId;
|
|
const deUint32 m_currentDeviceId;
|
|
};
|
|
|
|
VKWaiverTreeBuilder::VKWaiverTreeBuilder(const std::string& waiverFile,
|
|
const std::string& packageName,
|
|
const deUint32 currentVendor,
|
|
const deUint32 currentRenderer,
|
|
SessionInfo& sessionInfo,
|
|
std::vector<WaiverComponent>& waiverTree)
|
|
: WaiverTreeBuilder(waiverFile, packageName, "vendorId", "d", sessionInfo, waiverTree)
|
|
, m_currentVendorId(currentVendor)
|
|
, m_currentDeviceId(currentRenderer)
|
|
{
|
|
}
|
|
|
|
bool VKWaiverTreeBuilder::matchVendor(const std::string& vendor) const
|
|
{
|
|
return (m_currentVendorId == static_cast<deUint32>(std::stoul(vendor, 0, 0)));
|
|
}
|
|
|
|
bool VKWaiverTreeBuilder::matchDevice(const std::string& device) const
|
|
{
|
|
return (m_currentDeviceId == static_cast<deUint32>(std::stoul(device, 0, 0)));
|
|
}
|
|
|
|
void WaiverUtil::setup(const std::string waiverFile, std::string packageName, deUint32 vendorId, deUint32 deviceId, SessionInfo& sessionInfo)
|
|
{
|
|
VKWaiverTreeBuilder(waiverFile, packageName, vendorId, deviceId, sessionInfo, m_waiverTree).build();
|
|
}
|
|
|
|
void WaiverUtil::setup(const std::string waiverFile, std::string packageName, std::string vendor, std::string renderer, SessionInfo& sessionInfo)
|
|
{
|
|
GLWaiverTreeBuilder(waiverFile, packageName, vendor, renderer, sessionInfo, m_waiverTree).build();
|
|
}
|
|
|
|
bool WaiverUtil::isOnWaiverList(const std::string& casePath) const
|
|
{
|
|
if (m_waiverTree.empty())
|
|
return false;
|
|
|
|
// skip root e.g. "dEQP-VK"
|
|
size_t firstDotPos = casePath.find('.');
|
|
std::string::const_iterator componentStart = casePath.cbegin() + firstDotPos + 1;
|
|
std::string::const_iterator componentEnd = componentStart;
|
|
std::string::const_iterator pathEnd = casePath.cend();
|
|
const WaiverComponent* waiverComponent = m_waiverTree.data();
|
|
|
|
// check path component by component
|
|
while (true)
|
|
{
|
|
// find the last character of next component
|
|
++componentEnd;
|
|
for (; componentEnd < pathEnd ; ++componentEnd)
|
|
{
|
|
if (*componentEnd == '.')
|
|
break;
|
|
}
|
|
|
|
// check if one of children has the same component name
|
|
for (const auto& c : waiverComponent->children)
|
|
{
|
|
bool matchFound = tcu::matchWildcards(c->name.cbegin(),
|
|
c->name.cend(),
|
|
componentStart,
|
|
componentEnd,
|
|
false);
|
|
|
|
// current waiver component matches curent path component - go to next component
|
|
if (matchFound)
|
|
{
|
|
waiverComponent = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// we checked all components - if our pattern was a leaf then this test should be waived
|
|
if (componentEnd == pathEnd)
|
|
return waiverComponent->children.empty();
|
|
|
|
// go to next test path component
|
|
componentStart = componentEnd + 1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // vk
|