Path: blob/main_old/src/tests/test_expectations/GPUTestExpectationsParser.cpp
1693 views
//1// Copyright 2019 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//56#include "GPUTestExpectationsParser.h"78#include <stddef.h>9#include <stdint.h>10#include <string.h>1112#include "common/angleutils.h"13#include "common/debug.h"14#include "common/string_utils.h"1516namespace angle17{1819namespace20{2122enum LineParserStage23{24kLineParserBegin = 0,25kLineParserBugID,26kLineParserConfigs,27kLineParserColon,28kLineParserTestName,29kLineParserEqual,30kLineParserExpectations,31};3233enum Token34{35// os36kConfigWinXP = 0,37kConfigWinVista,38kConfigWin7,39kConfigWin8,40kConfigWin10,41kConfigWin,42kConfigMacLeopard,43kConfigMacSnowLeopard,44kConfigMacLion,45kConfigMacMountainLion,46kConfigMacMavericks,47kConfigMacYosemite,48kConfigMacElCapitan,49kConfigMacSierra,50kConfigMacHighSierra,51kConfigMacMojave,52kConfigMac,53kConfigIOS,54kConfigLinux,55kConfigChromeOS,56kConfigAndroid,57// gpu vendor58kConfigNVIDIA,59kConfigAMD,60kConfigIntel,61kConfigVMWare,62// build type63kConfigRelease,64kConfigDebug,65// ANGLE renderer66kConfigD3D9,67kConfigD3D11,68kConfigGLDesktop,69kConfigGLES,70kConfigVulkan,71kConfigSwiftShader,72kConfigMetal,73// Android devices74kConfigNexus5X,75kConfigPixel2,76kConfigPixel4,77// GPU devices78kConfigNVIDIAQuadroP400,79// PreRotation80kConfigPreRotation,81kConfigPreRotation90,82kConfigPreRotation180,83kConfigPreRotation270,84// SPIR-V generation85kConfigSPIRVGen,86// expectation87kExpectationPass,88kExpectationFail,89kExpectationFlaky,90kExpectationTimeout,91kExpectationSkip,92// separator93kSeparatorColon,94kSeparatorEqual,9596kNumberOfExactMatchTokens,9798// others99kTokenComment,100kTokenWord,101102kNumberOfTokens,103};104105enum ErrorType106{107kErrorFileIO = 0,108kErrorIllegalEntry,109kErrorInvalidEntry,110kErrorEntryWithExpectationConflicts,111kErrorEntryWithDisallowedExpectation,112kErrorEntriesOverlap,113114kNumberOfErrors,115};116117struct TokenInfo118{119constexpr TokenInfo()120: name(nullptr),121condition(GPUTestConfig::kConditionNone),122expectation(GPUTestExpectationsParser::kGpuTestPass)123{}124125constexpr TokenInfo(const char *nameIn,126GPUTestConfig::Condition conditionIn,127GPUTestExpectationsParser::GPUTestExpectation expectationIn)128: name(nameIn), condition(conditionIn), expectation(expectationIn)129{}130131constexpr TokenInfo(const char *nameIn, GPUTestConfig::Condition conditionIn)132: TokenInfo(nameIn, conditionIn, GPUTestExpectationsParser::kGpuTestPass)133{}134135const char *name;136GPUTestConfig::Condition condition;137GPUTestExpectationsParser::GPUTestExpectation expectation;138};139140constexpr TokenInfo kTokenData[kNumberOfTokens] = {141{"xp", GPUTestConfig::kConditionWinXP},142{"vista", GPUTestConfig::kConditionWinVista},143{"win7", GPUTestConfig::kConditionWin7},144{"win8", GPUTestConfig::kConditionWin8},145{"win10", GPUTestConfig::kConditionWin10},146{"win", GPUTestConfig::kConditionWin},147{"leopard", GPUTestConfig::kConditionMacLeopard},148{"snowleopard", GPUTestConfig::kConditionMacSnowLeopard},149{"lion", GPUTestConfig::kConditionMacLion},150{"mountainlion", GPUTestConfig::kConditionMacMountainLion},151{"mavericks", GPUTestConfig::kConditionMacMavericks},152{"yosemite", GPUTestConfig::kConditionMacYosemite},153{"elcapitan", GPUTestConfig::kConditionMacElCapitan},154{"sierra", GPUTestConfig::kConditionMacSierra},155{"highsierra", GPUTestConfig::kConditionMacHighSierra},156{"mojave", GPUTestConfig::kConditionMacMojave},157{"mac", GPUTestConfig::kConditionMac},158{"ios", GPUTestConfig::kConditionIOS},159{"linux", GPUTestConfig::kConditionLinux},160{"chromeos", GPUTestConfig::kConditionNone}, // https://anglebug.com/3363 CrOS not supported161{"android", GPUTestConfig::kConditionAndroid},162{"nvidia", GPUTestConfig::kConditionNVIDIA},163{"amd", GPUTestConfig::kConditionAMD},164{"intel", GPUTestConfig::kConditionIntel},165{"vmware", GPUTestConfig::kConditionVMWare},166{"release", GPUTestConfig::kConditionRelease},167{"debug", GPUTestConfig::kConditionDebug},168{"d3d9", GPUTestConfig::kConditionD3D9},169{"d3d11", GPUTestConfig::kConditionD3D11},170{"opengl", GPUTestConfig::kConditionGLDesktop},171{"gles", GPUTestConfig::kConditionGLES},172{"vulkan", GPUTestConfig::kConditionVulkan},173{"swiftshader", GPUTestConfig::kConditionSwiftShader},174{"metal", GPUTestConfig::kConditionMetal},175{"nexus5x", GPUTestConfig::kConditionNexus5X},176{"pixel2orxl", GPUTestConfig::kConditionPixel2OrXL},177{"pixel4orxl", GPUTestConfig::kConditionPixel4OrXL},178{"quadrop400", GPUTestConfig::kConditionNVIDIAQuadroP400},179{"prerotation", GPUTestConfig::kConditionPreRotation},180{"prerotation90", GPUTestConfig::kConditionPreRotation90},181{"prerotation180", GPUTestConfig::kConditionPreRotation180},182{"prerotation270", GPUTestConfig::kConditionPreRotation270},183{"spirvgen", GPUTestConfig::kConditionSPIRVGen},184{"pass", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestPass},185{"fail", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestFail},186{"flaky", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestFlaky},187{"timeout", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestTimeout},188{"skip", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestSkip},189{":", GPUTestConfig::kConditionNone}, // kSeparatorColon190{"=", GPUTestConfig::kConditionNone}, // kSeparatorEqual191{}, // kNumberOfExactMatchTokens192{}, // kTokenComment193{}, // kTokenWord194};195196const char *kErrorMessage[kNumberOfErrors] = {197"file IO failed",198"entry with wrong format",199"entry invalid, likely unimplemented modifiers",200"entry with expectation modifier conflicts",201"entry with unsupported expectation",202"two entries' configs overlap",203};204205inline bool StartsWithASCII(const std::string &str, const std::string &search, bool caseSensitive)206{207ASSERT(!caseSensitive);208return str.compare(0, search.length(), search) == 0;209}210211template <class Char>212inline Char ToLowerASCII(Char c)213{214return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;215}216217template <typename Iter>218inline bool DoLowerCaseEqualsASCII(Iter a_begin, Iter a_end, const char *b)219{220for (Iter it = a_begin; it != a_end; ++it, ++b)221{222if (!*b || ToLowerASCII(*it) != *b)223return false;224}225return *b == 0;226}227228inline bool LowerCaseEqualsASCII(const std::string &a, const char *b)229{230return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);231}232233inline Token ParseToken(const std::string &word)234{235if (StartsWithASCII(word, "//", false))236return kTokenComment;237238for (int32_t i = 0; i < kNumberOfExactMatchTokens; ++i)239{240if (LowerCaseEqualsASCII(word, kTokenData[i].name))241return static_cast<Token>(i);242}243return kTokenWord;244}245246bool ConditionArrayIsSubset(const GPUTestConfig::ConditionArray &subset,247const GPUTestConfig::ConditionArray &superset)248{249for (size_t subsetCondition : subset)250{251bool foundCondition = false;252for (size_t supersetCondition : superset)253{254if (subsetCondition == supersetCondition)255{256foundCondition = true;257break;258}259}260261if (!foundCondition)262{263return false;264}265}266267return true;268}269270// If one array is completely contained within the other, then we say the conditions overlap.271bool ConditionsOverlap(const GPUTestConfig::ConditionArray &conditionsI,272const GPUTestConfig::ConditionArray &conditionsJ)273{274return ConditionArrayIsSubset(conditionsI, conditionsJ) ||275ConditionArrayIsSubset(conditionsJ, conditionsI);276}277} // anonymous namespace278279const char *GetConditionName(uint32_t condition)280{281if (condition == GPUTestConfig::kConditionNone)282{283return nullptr;284}285286for (const TokenInfo &info : kTokenData)287{288if (info.condition == condition)289{290// kConditionNone is used to tag tokens that aren't conditions, but this case has been291// handled above.292ASSERT(info.condition != GPUTestConfig::kConditionNone);293return info.name;294}295}296297return nullptr;298}299300GPUTestExpectationsParser::GPUTestExpectationsParser()301: mExpectationsAllowMask(302GPUTestExpectationsParser::kGpuTestPass | GPUTestExpectationsParser::kGpuTestFail |303GPUTestExpectationsParser::kGpuTestFlaky | GPUTestExpectationsParser::kGpuTestTimeout |304GPUTestExpectationsParser::kGpuTestSkip)305{306// Some initial checks.307ASSERT((static_cast<unsigned int>(kNumberOfTokens)) ==308(sizeof(kTokenData) / sizeof(kTokenData[0])));309ASSERT((static_cast<unsigned int>(kNumberOfErrors)) ==310(sizeof(kErrorMessage) / sizeof(kErrorMessage[0])));311}312313GPUTestExpectationsParser::~GPUTestExpectationsParser() = default;314315bool GPUTestExpectationsParser::loadTestExpectationsImpl(const GPUTestConfig *config,316const std::string &data)317{318mEntries.clear();319mErrorMessages.clear();320321std::vector<std::string> lines = SplitString(data, "\n", TRIM_WHITESPACE, SPLIT_WANT_ALL);322bool rt = true;323for (size_t i = 0; i < lines.size(); ++i)324{325if (!parseLine(config, lines[i], i + 1))326rt = false;327}328if (detectConflictsBetweenEntries())329{330mEntries.clear();331rt = false;332}333334return rt;335}336337bool GPUTestExpectationsParser::loadTestExpectations(const GPUTestConfig &config,338const std::string &data)339{340return loadTestExpectationsImpl(&config, data);341}342343bool GPUTestExpectationsParser::loadAllTestExpectations(const std::string &data)344{345return loadTestExpectationsImpl(nullptr, data);346}347348bool GPUTestExpectationsParser::loadTestExpectationsFromFileImpl(const GPUTestConfig *config,349const std::string &path)350{351mEntries.clear();352mErrorMessages.clear();353354std::string data;355if (!ReadFileToString(path, &data))356{357mErrorMessages.push_back(kErrorMessage[kErrorFileIO]);358return false;359}360return loadTestExpectationsImpl(config, data);361}362363bool GPUTestExpectationsParser::loadTestExpectationsFromFile(const GPUTestConfig &config,364const std::string &path)365{366return loadTestExpectationsFromFileImpl(&config, path);367}368369bool GPUTestExpectationsParser::loadAllTestExpectationsFromFile(const std::string &path)370{371return loadTestExpectationsFromFileImpl(nullptr, path);372}373374int32_t GPUTestExpectationsParser::getTestExpectationImpl(const GPUTestConfig *config,375const std::string &testName)376{377size_t maxExpectationLen = 0;378GPUTestExpectationEntry *foundEntry = nullptr;379for (GPUTestExpectationEntry &entry : mEntries)380{381if (NamesMatchWithWildcard(entry.testName.c_str(), testName.c_str()))382{383size_t expectationLen = entry.testName.length();384385// Filter by condition first.386bool satisfiesConditions = true;387if (config)388{389for (size_t condition : entry.conditions)390{391if (!config->getConditions()[condition])392{393satisfiesConditions = false;394break;395}396}397}398399// The longest/most specific matching expectation overrides any others.400if (satisfiesConditions && expectationLen > maxExpectationLen)401{402maxExpectationLen = expectationLen;403foundEntry = &entry;404}405}406}407if (foundEntry != nullptr)408{409foundEntry->used = true;410return foundEntry->testExpectation;411}412return kGpuTestPass;413}414415int32_t GPUTestExpectationsParser::getTestExpectation(const std::string &testName)416{417return getTestExpectationImpl(nullptr, testName);418}419420int32_t GPUTestExpectationsParser::getTestExpectationWithConfig(const GPUTestConfig &config,421const std::string &testName)422{423return getTestExpectationImpl(&config, testName);424}425426const std::vector<std::string> &GPUTestExpectationsParser::getErrorMessages() const427{428return mErrorMessages;429}430431std::vector<std::string> GPUTestExpectationsParser::getUnusedExpectationsMessages() const432{433std::vector<std::string> messages;434std::vector<GPUTestExpectationsParser::GPUTestExpectationEntry> unusedExpectations =435getUnusedExpectations();436for (size_t i = 0; i < unusedExpectations.size(); ++i)437{438std::string message =439"Line " + ToString(unusedExpectations[i].lineNumber) + ": expectation was unused.";440messages.push_back(message);441}442return messages;443}444445bool GPUTestExpectationsParser::parseLine(const GPUTestConfig *config,446const std::string &lineData,447size_t lineNumber)448{449std::vector<std::string> tokens =450SplitString(lineData, kWhitespaceASCII, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);451int32_t stage = kLineParserBegin;452GPUTestExpectationEntry entry;453entry.lineNumber = lineNumber;454entry.used = false;455bool skipLine = false;456for (size_t i = 0; i < tokens.size() && !skipLine; ++i)457{458Token token = ParseToken(tokens[i]);459switch (token)460{461case kTokenComment:462skipLine = true;463break;464case kConfigWinXP:465case kConfigWinVista:466case kConfigWin7:467case kConfigWin8:468case kConfigWin10:469case kConfigWin:470case kConfigMacLeopard:471case kConfigMacSnowLeopard:472case kConfigMacLion:473case kConfigMacMountainLion:474case kConfigMacMavericks:475case kConfigMacYosemite:476case kConfigMacElCapitan:477case kConfigMacSierra:478case kConfigMacHighSierra:479case kConfigMacMojave:480case kConfigMac:481case kConfigIOS:482case kConfigLinux:483case kConfigChromeOS:484case kConfigAndroid:485case kConfigNVIDIA:486case kConfigAMD:487case kConfigIntel:488case kConfigVMWare:489case kConfigRelease:490case kConfigDebug:491case kConfigD3D9:492case kConfigD3D11:493case kConfigGLDesktop:494case kConfigGLES:495case kConfigVulkan:496case kConfigSwiftShader:497case kConfigMetal:498case kConfigNexus5X:499case kConfigPixel2:500case kConfigPixel4:501case kConfigNVIDIAQuadroP400:502case kConfigPreRotation:503case kConfigPreRotation90:504case kConfigPreRotation180:505case kConfigPreRotation270:506case kConfigSPIRVGen:507// MODIFIERS, check each condition and add accordingly.508if (stage != kLineParserConfigs && stage != kLineParserBugID)509{510pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);511return false;512}513{514bool err = false;515if (config)516{517if (!checkTokenCondition(*config, err, token, lineNumber))518{519skipLine = true; // Move to the next line without adding this one.520}521}522else523{524// Store the conditions for later comparison if we don't have a config.525entry.conditions[kTokenData[token].condition] = true;526}527if (err)528{529return false;530}531}532if (stage == kLineParserBugID)533{534stage++;535}536break;537case kSeparatorColon:538// :539// If there are no modifiers, move straight to separator colon540if (stage == kLineParserBugID)541{542stage++;543}544if (stage != kLineParserConfigs)545{546pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);547return false;548}549stage++;550break;551case kSeparatorEqual:552// =553if (stage != kLineParserTestName)554{555pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);556return false;557}558stage++;559break;560case kTokenWord:561// BUG_ID or TEST_NAME562if (stage == kLineParserBegin)563{564// Bug ID is not used for anything; ignore it.565}566else if (stage == kLineParserColon)567{568entry.testName = tokens[i];569}570else571{572pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);573return false;574}575stage++;576break;577case kExpectationPass:578case kExpectationFail:579case kExpectationFlaky:580case kExpectationTimeout:581case kExpectationSkip:582// TEST_EXPECTATIONS583if (stage != kLineParserEqual && stage != kLineParserExpectations)584{585pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);586return false;587}588if (entry.testExpectation != 0)589{590pushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],591lineNumber);592return false;593}594if ((mExpectationsAllowMask & kTokenData[token].expectation) == 0)595{596pushErrorMessage(kErrorMessage[kErrorEntryWithDisallowedExpectation],597lineNumber);598return false;599}600entry.testExpectation = kTokenData[token].expectation;601if (stage == kLineParserEqual)602stage++;603break;604default:605ASSERT(false);606break;607}608}609if (stage == kLineParserBegin || skipLine)610{611// The whole line is empty or all comments, or has been skipped to to a condition token.612return true;613}614if (stage == kLineParserExpectations)615{616mEntries.push_back(entry);617return true;618}619pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);620return false;621}622623bool GPUTestExpectationsParser::checkTokenCondition(const GPUTestConfig &config,624bool &err,625int32_t token,626size_t lineNumber)627{628if (token >= kNumberOfTokens)629{630pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);631err = true;632return false;633}634635if (kTokenData[token].condition == GPUTestConfig::kConditionNone ||636kTokenData[token].condition >= GPUTestConfig::kNumberOfConditions)637{638pushErrorMessage(kErrorMessage[kErrorInvalidEntry], lineNumber);639// error on any unsupported conditions640err = true;641return false;642}643err = false;644return config.getConditions()[kTokenData[token].condition];645}646647bool GPUTestExpectationsParser::detectConflictsBetweenEntries()648{649bool rt = false;650for (size_t i = 0; i < mEntries.size(); ++i)651{652for (size_t j = i + 1; j < mEntries.size(); ++j)653{654const GPUTestExpectationEntry &entryI = mEntries[i];655const GPUTestExpectationEntry &entryJ = mEntries[j];656if (entryI.testName == entryJ.testName &&657ConditionsOverlap(entryI.conditions, entryJ.conditions))658{659pushErrorMessage(kErrorMessage[kErrorEntriesOverlap], entryI.lineNumber,660entryJ.lineNumber);661rt = true;662}663}664}665return rt;666}667668std::vector<GPUTestExpectationsParser::GPUTestExpectationEntry>669GPUTestExpectationsParser::getUnusedExpectations() const670{671std::vector<GPUTestExpectationsParser::GPUTestExpectationEntry> unusedExpectations;672for (size_t i = 0; i < mEntries.size(); ++i)673{674if (!mEntries[i].used)675{676unusedExpectations.push_back(mEntries[i]);677}678}679return unusedExpectations;680}681682void GPUTestExpectationsParser::pushErrorMessage(const std::string &message, size_t lineNumber)683{684mErrorMessages.push_back("Line " + ToString(lineNumber) + " : " + message.c_str());685}686687void GPUTestExpectationsParser::pushErrorMessage(const std::string &message,688size_t entry1LineNumber,689size_t entry2LineNumber)690{691mErrorMessages.push_back("Line " + ToString(entry1LineNumber) + " and " +692ToString(entry2LineNumber) + " : " + message.c_str());693}694695GPUTestExpectationsParser::GPUTestExpectationEntry::GPUTestExpectationEntry()696: testExpectation(0), lineNumber(0)697{}698699} // namespace angle700701702