• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "GPUTestExpectationsParser.h"
8 
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12 
13 #include "common/angleutils.h"
14 #include "common/debug.h"
15 #include "common/string_utils.h"
16 
17 namespace angle
18 {
19 
20 namespace
21 {
22 
23 enum LineParserStage
24 {
25     kLineParserBegin = 0,
26     kLineParserBugID,
27     kLineParserConfigs,
28     kLineParserColon,
29     kLineParserTestName,
30     kLineParserEqual,
31     kLineParserExpectations,
32 };
33 
34 enum Token
35 {
36     // os
37     kConfigWinXP = 0,
38     kConfigWinVista,
39     kConfigWin7,
40     kConfigWin8,
41     kConfigWin10,
42     kConfigWin,
43     kConfigMacLeopard,
44     kConfigMacSnowLeopard,
45     kConfigMacLion,
46     kConfigMacMountainLion,
47     kConfigMacMavericks,
48     kConfigMacYosemite,
49     kConfigMacElCapitan,
50     kConfigMacSierra,
51     kConfigMacHighSierra,
52     kConfigMacMojave,
53     kConfigMac,
54     kConfigIOS,
55     kConfigLinux,
56     kConfigChromeOS,
57     kConfigAndroid,
58     // gpu vendor
59     kConfigNVIDIA,
60     kConfigAMD,
61     kConfigIntel,
62     kConfigVMWare,
63     kConfigApple,
64     // build type
65     kConfigRelease,
66     kConfigDebug,
67     // ANGLE renderer
68     kConfigD3D9,
69     kConfigD3D11,
70     kConfigGLDesktop,
71     kConfigGLES,
72     kConfigVulkan,
73     kConfigSwiftShader,
74     kConfigMetal,
75     // Android devices
76     kConfigNexus5X,
77     kConfigPixel2,
78     kConfigPixel4,
79     // GPU devices
80     kConfigNVIDIAQuadroP400,
81     kConfigNVIDIAGTX1660,
82     // PreRotation
83     kConfigPreRotation,
84     kConfigPreRotation90,
85     kConfigPreRotation180,
86     kConfigPreRotation270,
87     // Sanitizers
88     kConfigNoSan,
89     kConfigASan,
90     kConfigTSan,
91     kConfigUBSan,
92     // expectation
93     kExpectationPass,
94     kExpectationFail,
95     kExpectationFlaky,
96     kExpectationTimeout,
97     kExpectationSkip,
98     // separator
99     kSeparatorColon,
100     kSeparatorEqual,
101 
102     kNumberOfExactMatchTokens,
103 
104     // others
105     kTokenComment,
106     kTokenWord,
107 
108     kNumberOfTokens,
109 };
110 
111 enum ErrorType
112 {
113     kErrorFileIO = 0,
114     kErrorIllegalEntry,
115     kErrorInvalidEntry,
116     kErrorEntryWithExpectationConflicts,
117     kErrorEntryWithDisallowedExpectation,
118     kErrorEntriesOverlap,
119 
120     kNumberOfErrors,
121 };
122 
123 struct TokenInfo
124 {
TokenInfoangle::__anonc3cef3ab0111::TokenInfo125     constexpr TokenInfo()
126         : name(nullptr),
127           condition(GPUTestConfig::kConditionNone),
128           expectation(GPUTestExpectationsParser::kGpuTestPass)
129     {}
130 
TokenInfoangle::__anonc3cef3ab0111::TokenInfo131     constexpr TokenInfo(const char *nameIn,
132                         GPUTestConfig::Condition conditionIn,
133                         GPUTestExpectationsParser::GPUTestExpectation expectationIn)
134         : name(nameIn), condition(conditionIn), expectation(expectationIn)
135     {}
136 
TokenInfoangle::__anonc3cef3ab0111::TokenInfo137     constexpr TokenInfo(const char *nameIn, GPUTestConfig::Condition conditionIn)
138         : TokenInfo(nameIn, conditionIn, GPUTestExpectationsParser::kGpuTestPass)
139     {}
140 
141     const char *name;
142     GPUTestConfig::Condition condition;
143     GPUTestExpectationsParser::GPUTestExpectation expectation;
144 };
145 
146 constexpr TokenInfo kTokenData[kNumberOfTokens] = {
147     {"xp", GPUTestConfig::kConditionWinXP},
148     {"vista", GPUTestConfig::kConditionWinVista},
149     {"win7", GPUTestConfig::kConditionWin7},
150     {"win8", GPUTestConfig::kConditionWin8},
151     {"win10", GPUTestConfig::kConditionWin10},
152     {"win", GPUTestConfig::kConditionWin},
153     {"leopard", GPUTestConfig::kConditionMacLeopard},
154     {"snowleopard", GPUTestConfig::kConditionMacSnowLeopard},
155     {"lion", GPUTestConfig::kConditionMacLion},
156     {"mountainlion", GPUTestConfig::kConditionMacMountainLion},
157     {"mavericks", GPUTestConfig::kConditionMacMavericks},
158     {"yosemite", GPUTestConfig::kConditionMacYosemite},
159     {"elcapitan", GPUTestConfig::kConditionMacElCapitan},
160     {"sierra", GPUTestConfig::kConditionMacSierra},
161     {"highsierra", GPUTestConfig::kConditionMacHighSierra},
162     {"mojave", GPUTestConfig::kConditionMacMojave},
163     {"mac", GPUTestConfig::kConditionMac},
164     {"ios", GPUTestConfig::kConditionIOS},
165     {"linux", GPUTestConfig::kConditionLinux},
166     {"chromeos", GPUTestConfig::kConditionNone},  // https://anglebug.com/3363 CrOS not supported
167     {"android", GPUTestConfig::kConditionAndroid},
168     {"nvidia", GPUTestConfig::kConditionNVIDIA},
169     {"amd", GPUTestConfig::kConditionAMD},
170     {"intel", GPUTestConfig::kConditionIntel},
171     {"vmware", GPUTestConfig::kConditionVMWare},
172     {"apple", GPUTestConfig::kConditionApple},
173     {"release", GPUTestConfig::kConditionRelease},
174     {"debug", GPUTestConfig::kConditionDebug},
175     {"d3d9", GPUTestConfig::kConditionD3D9},
176     {"d3d11", GPUTestConfig::kConditionD3D11},
177     {"opengl", GPUTestConfig::kConditionGLDesktop},
178     {"gles", GPUTestConfig::kConditionGLES},
179     {"vulkan", GPUTestConfig::kConditionVulkan},
180     {"swiftshader", GPUTestConfig::kConditionSwiftShader},
181     {"metal", GPUTestConfig::kConditionMetal},
182     {"nexus5x", GPUTestConfig::kConditionNexus5X},
183     {"pixel2orxl", GPUTestConfig::kConditionPixel2OrXL},
184     {"pixel4orxl", GPUTestConfig::kConditionPixel4OrXL},
185     {"quadrop400", GPUTestConfig::kConditionNVIDIAQuadroP400},
186     {"gtx1660", GPUTestConfig::kConditionNVIDIAGTX1660},
187     {"prerotation", GPUTestConfig::kConditionPreRotation},
188     {"prerotation90", GPUTestConfig::kConditionPreRotation90},
189     {"prerotation180", GPUTestConfig::kConditionPreRotation180},
190     {"prerotation270", GPUTestConfig::kConditionPreRotation270},
191     {"nosan", GPUTestConfig::kConditionNoSan},
192     {"asan", GPUTestConfig::kConditionASan},
193     {"tsan", GPUTestConfig::kConditionTSan},
194     {"ubsan", GPUTestConfig::kConditionUBSan},
195     {"pass", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestPass},
196     {"fail", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestFail},
197     {"flaky", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestFlaky},
198     {"timeout", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestTimeout},
199     {"skip", GPUTestConfig::kConditionNone, GPUTestExpectationsParser::kGpuTestSkip},
200     {":", GPUTestConfig::kConditionNone},  // kSeparatorColon
201     {"=", GPUTestConfig::kConditionNone},  // kSeparatorEqual
202     {},                                    // kNumberOfExactMatchTokens
203     {},                                    // kTokenComment
204     {},                                    // kTokenWord
205 };
206 
207 const char *kErrorMessage[kNumberOfErrors] = {
208     "file IO failed",
209     "entry with wrong format",
210     "entry invalid, likely unimplemented modifiers",
211     "entry with expectation modifier conflicts",
212     "entry with unsupported expectation",
213     "two entries' configs overlap",
214 };
215 
StartsWithASCII(const std::string & str,const std::string & search,bool caseSensitive)216 inline bool StartsWithASCII(const std::string &str, const std::string &search, bool caseSensitive)
217 {
218     ASSERT(!caseSensitive);
219     return str.compare(0, search.length(), search) == 0;
220 }
221 
222 template <class Char>
ToLowerASCII(Char c)223 inline Char ToLowerASCII(Char c)
224 {
225     return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
226 }
227 
228 template <typename Iter>
DoLowerCaseEqualsASCII(Iter a_begin,Iter a_end,const char * b)229 inline bool DoLowerCaseEqualsASCII(Iter a_begin, Iter a_end, const char *b)
230 {
231     for (Iter it = a_begin; it != a_end; ++it, ++b)
232     {
233         if (!*b || ToLowerASCII(*it) != *b)
234             return false;
235     }
236     return *b == 0;
237 }
238 
LowerCaseEqualsASCII(const std::string & a,const char * b)239 inline bool LowerCaseEqualsASCII(const std::string &a, const char *b)
240 {
241     return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
242 }
243 
ParseToken(const std::string & word)244 inline Token ParseToken(const std::string &word)
245 {
246     if (StartsWithASCII(word, "//", false))
247         return kTokenComment;
248 
249     for (int32_t i = 0; i < kNumberOfExactMatchTokens; ++i)
250     {
251         if (LowerCaseEqualsASCII(word, kTokenData[i].name))
252             return static_cast<Token>(i);
253     }
254     return kTokenWord;
255 }
256 
ConditionArrayIsSubset(const GPUTestConfig::ConditionArray & subset,const GPUTestConfig::ConditionArray & superset)257 bool ConditionArrayIsSubset(const GPUTestConfig::ConditionArray &subset,
258                             const GPUTestConfig::ConditionArray &superset)
259 {
260     for (size_t subsetCondition : subset)
261     {
262         bool foundCondition = false;
263         for (size_t supersetCondition : superset)
264         {
265             if (subsetCondition == supersetCondition)
266             {
267                 foundCondition = true;
268                 break;
269             }
270         }
271 
272         if (!foundCondition)
273         {
274             return false;
275         }
276     }
277 
278     return true;
279 }
280 
281 // If one array is completely contained within the other, then we say the conditions overlap.
ConditionsOverlap(const GPUTestConfig::ConditionArray & conditionsI,const GPUTestConfig::ConditionArray & conditionsJ)282 bool ConditionsOverlap(const GPUTestConfig::ConditionArray &conditionsI,
283                        const GPUTestConfig::ConditionArray &conditionsJ)
284 {
285     return ConditionArrayIsSubset(conditionsI, conditionsJ) ||
286            ConditionArrayIsSubset(conditionsJ, conditionsI);
287 }
288 }  // anonymous namespace
289 
GetConditionName(uint32_t condition)290 const char *GetConditionName(uint32_t condition)
291 {
292     if (condition == GPUTestConfig::kConditionNone)
293     {
294         return nullptr;
295     }
296 
297     for (const TokenInfo &info : kTokenData)
298     {
299         if (info.condition == condition)
300         {
301             // kConditionNone is used to tag tokens that aren't conditions, but this case has been
302             // handled above.
303             ASSERT(info.condition != GPUTestConfig::kConditionNone);
304             return info.name;
305         }
306     }
307 
308     return nullptr;
309 }
310 
GPUTestExpectationsParser()311 GPUTestExpectationsParser::GPUTestExpectationsParser()
312     : mExpectationsAllowMask(
313           GPUTestExpectationsParser::kGpuTestPass | GPUTestExpectationsParser::kGpuTestFail |
314           GPUTestExpectationsParser::kGpuTestFlaky | GPUTestExpectationsParser::kGpuTestTimeout |
315           GPUTestExpectationsParser::kGpuTestSkip)
316 {
317     // Some initial checks.
318     ASSERT((static_cast<unsigned int>(kNumberOfTokens)) ==
319            (sizeof(kTokenData) / sizeof(kTokenData[0])));
320     ASSERT((static_cast<unsigned int>(kNumberOfErrors)) ==
321            (sizeof(kErrorMessage) / sizeof(kErrorMessage[0])));
322 }
323 
324 GPUTestExpectationsParser::~GPUTestExpectationsParser() = default;
325 
loadTestExpectationsImpl(const GPUTestConfig * config,const std::string & data)326 bool GPUTestExpectationsParser::loadTestExpectationsImpl(const GPUTestConfig *config,
327                                                          const std::string &data)
328 {
329     mEntries.clear();
330     mErrorMessages.clear();
331 
332     std::vector<std::string> lines = SplitString(data, "\n", TRIM_WHITESPACE, SPLIT_WANT_ALL);
333     bool rt                        = true;
334     for (size_t i = 0; i < lines.size(); ++i)
335     {
336         if (!parseLine(config, lines[i], i + 1))
337             rt = false;
338     }
339     if (detectConflictsBetweenEntries())
340     {
341         mEntries.clear();
342         rt = false;
343     }
344 
345     return rt;
346 }
347 
loadTestExpectations(const GPUTestConfig & config,const std::string & data)348 bool GPUTestExpectationsParser::loadTestExpectations(const GPUTestConfig &config,
349                                                      const std::string &data)
350 {
351     return loadTestExpectationsImpl(&config, data);
352 }
353 
loadAllTestExpectations(const std::string & data)354 bool GPUTestExpectationsParser::loadAllTestExpectations(const std::string &data)
355 {
356     return loadTestExpectationsImpl(nullptr, data);
357 }
358 
loadTestExpectationsFromFileImpl(const GPUTestConfig * config,const std::string & path)359 bool GPUTestExpectationsParser::loadTestExpectationsFromFileImpl(const GPUTestConfig *config,
360                                                                  const std::string &path)
361 {
362     mEntries.clear();
363     mErrorMessages.clear();
364 
365     std::string data;
366     if (!ReadFileToString(path, &data))
367     {
368         mErrorMessages.push_back(kErrorMessage[kErrorFileIO]);
369         return false;
370     }
371     return loadTestExpectationsImpl(config, data);
372 }
373 
loadTestExpectationsFromFile(const GPUTestConfig & config,const std::string & path)374 bool GPUTestExpectationsParser::loadTestExpectationsFromFile(const GPUTestConfig &config,
375                                                              const std::string &path)
376 {
377     return loadTestExpectationsFromFileImpl(&config, path);
378 }
379 
loadAllTestExpectationsFromFile(const std::string & path)380 bool GPUTestExpectationsParser::loadAllTestExpectationsFromFile(const std::string &path)
381 {
382     return loadTestExpectationsFromFileImpl(nullptr, path);
383 }
384 
getTestExpectationImpl(const GPUTestConfig * config,const std::string & testName)385 int32_t GPUTestExpectationsParser::getTestExpectationImpl(const GPUTestConfig *config,
386                                                           const std::string &testName)
387 {
388     size_t maxExpectationLen            = 0;
389     GPUTestExpectationEntry *foundEntry = nullptr;
390     for (GPUTestExpectationEntry &entry : mEntries)
391     {
392         if (NamesMatchWithWildcard(entry.testName.c_str(), testName.c_str()))
393         {
394             size_t expectationLen = entry.testName.length();
395 
396             // Filter by condition first.
397             bool satisfiesConditions = true;
398             if (config)
399             {
400                 for (size_t condition : entry.conditions)
401                 {
402                     if (!config->getConditions()[condition])
403                     {
404                         satisfiesConditions = false;
405                         break;
406                     }
407                 }
408             }
409 
410             // The longest/most specific matching expectation overrides any others.
411             if (satisfiesConditions && expectationLen > maxExpectationLen)
412             {
413                 maxExpectationLen = expectationLen;
414                 foundEntry        = &entry;
415             }
416         }
417     }
418     if (foundEntry != nullptr)
419     {
420         foundEntry->used = true;
421         return foundEntry->testExpectation;
422     }
423     return kGpuTestPass;
424 }
425 
getTestExpectation(const std::string & testName)426 int32_t GPUTestExpectationsParser::getTestExpectation(const std::string &testName)
427 {
428     return getTestExpectationImpl(nullptr, testName);
429 }
430 
getTestExpectationWithConfig(const GPUTestConfig & config,const std::string & testName)431 int32_t GPUTestExpectationsParser::getTestExpectationWithConfig(const GPUTestConfig &config,
432                                                                 const std::string &testName)
433 {
434     return getTestExpectationImpl(&config, testName);
435 }
436 
getErrorMessages() const437 const std::vector<std::string> &GPUTestExpectationsParser::getErrorMessages() const
438 {
439     return mErrorMessages;
440 }
441 
getUnusedExpectationsMessages() const442 std::vector<std::string> GPUTestExpectationsParser::getUnusedExpectationsMessages() const
443 {
444     std::vector<std::string> messages;
445     std::vector<GPUTestExpectationsParser::GPUTestExpectationEntry> unusedExpectations =
446         getUnusedExpectations();
447     for (size_t i = 0; i < unusedExpectations.size(); ++i)
448     {
449         std::string message =
450             "Line " + ToString(unusedExpectations[i].lineNumber) + ": expectation was unused.";
451         messages.push_back(message);
452     }
453     return messages;
454 }
455 
parseLine(const GPUTestConfig * config,const std::string & lineData,size_t lineNumber)456 bool GPUTestExpectationsParser::parseLine(const GPUTestConfig *config,
457                                           const std::string &lineData,
458                                           size_t lineNumber)
459 {
460     std::vector<std::string> tokens =
461         SplitString(lineData, kWhitespaceASCII, KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
462     int32_t stage = kLineParserBegin;
463     GPUTestExpectationEntry entry;
464     entry.lineNumber = lineNumber;
465     entry.used       = false;
466     bool skipLine    = false;
467     for (size_t i = 0; i < tokens.size() && !skipLine; ++i)
468     {
469         Token token = ParseToken(tokens[i]);
470         switch (token)
471         {
472             case kTokenComment:
473                 skipLine = true;
474                 break;
475             case kConfigWinXP:
476             case kConfigWinVista:
477             case kConfigWin7:
478             case kConfigWin8:
479             case kConfigWin10:
480             case kConfigWin:
481             case kConfigMacLeopard:
482             case kConfigMacSnowLeopard:
483             case kConfigMacLion:
484             case kConfigMacMountainLion:
485             case kConfigMacMavericks:
486             case kConfigMacYosemite:
487             case kConfigMacElCapitan:
488             case kConfigMacSierra:
489             case kConfigMacHighSierra:
490             case kConfigMacMojave:
491             case kConfigMac:
492             case kConfigIOS:
493             case kConfigLinux:
494             case kConfigChromeOS:
495             case kConfigAndroid:
496             case kConfigNVIDIA:
497             case kConfigAMD:
498             case kConfigIntel:
499             case kConfigVMWare:
500             case kConfigApple:
501             case kConfigRelease:
502             case kConfigDebug:
503             case kConfigD3D9:
504             case kConfigD3D11:
505             case kConfigGLDesktop:
506             case kConfigGLES:
507             case kConfigVulkan:
508             case kConfigSwiftShader:
509             case kConfigMetal:
510             case kConfigNexus5X:
511             case kConfigPixel2:
512             case kConfigPixel4:
513             case kConfigNVIDIAQuadroP400:
514             case kConfigNVIDIAGTX1660:
515             case kConfigPreRotation:
516             case kConfigPreRotation90:
517             case kConfigPreRotation180:
518             case kConfigPreRotation270:
519             case kConfigNoSan:
520             case kConfigASan:
521             case kConfigTSan:
522             case kConfigUBSan:
523                 // MODIFIERS, check each condition and add accordingly.
524                 if (stage != kLineParserConfigs && stage != kLineParserBugID)
525                 {
526                     pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
527                     return false;
528                 }
529                 {
530                     bool err = false;
531                     if (config)
532                     {
533                         if (!checkTokenCondition(*config, err, token, lineNumber))
534                         {
535                             skipLine = true;  // Move to the next line without adding this one.
536                         }
537                     }
538                     else
539                     {
540                         // Store the conditions for later comparison if we don't have a config.
541                         entry.conditions[kTokenData[token].condition] = true;
542                     }
543                     if (err)
544                     {
545                         return false;
546                     }
547                 }
548                 if (stage == kLineParserBugID)
549                 {
550                     stage++;
551                 }
552                 break;
553             case kSeparatorColon:
554                 // :
555                 // If there are no modifiers, move straight to separator colon
556                 if (stage == kLineParserBugID)
557                 {
558                     stage++;
559                 }
560                 if (stage != kLineParserConfigs)
561                 {
562                     pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
563                     return false;
564                 }
565                 stage++;
566                 break;
567             case kSeparatorEqual:
568                 // =
569                 if (stage != kLineParserTestName)
570                 {
571                     pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
572                     return false;
573                 }
574                 stage++;
575                 break;
576             case kTokenWord:
577                 // BUG_ID or TEST_NAME
578                 if (stage == kLineParserBegin)
579                 {
580                     // Bug ID is not used for anything; ignore it.
581                 }
582                 else if (stage == kLineParserColon)
583                 {
584                     entry.testName = tokens[i];
585                 }
586                 else
587                 {
588                     pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
589                     return false;
590                 }
591                 stage++;
592                 break;
593             case kExpectationPass:
594             case kExpectationFail:
595             case kExpectationFlaky:
596             case kExpectationTimeout:
597             case kExpectationSkip:
598                 // TEST_EXPECTATIONS
599                 if (stage != kLineParserEqual && stage != kLineParserExpectations)
600                 {
601                     pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
602                     return false;
603                 }
604                 if (entry.testExpectation != 0)
605                 {
606                     pushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
607                                      lineNumber);
608                     return false;
609                 }
610                 if ((mExpectationsAllowMask & kTokenData[token].expectation) == 0)
611                 {
612                     pushErrorMessage(kErrorMessage[kErrorEntryWithDisallowedExpectation],
613                                      lineNumber);
614                     return false;
615                 }
616                 entry.testExpectation = kTokenData[token].expectation;
617                 if (stage == kLineParserEqual)
618                     stage++;
619                 break;
620             default:
621                 ASSERT(false);
622                 break;
623         }
624     }
625     if (stage == kLineParserBegin || skipLine)
626     {
627         // The whole line is empty or all comments, or has been skipped to to a condition token.
628         return true;
629     }
630     if (stage == kLineParserExpectations)
631     {
632         mEntries.push_back(entry);
633         return true;
634     }
635     pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
636     return false;
637 }
638 
checkTokenCondition(const GPUTestConfig & config,bool & err,int32_t token,size_t lineNumber)639 bool GPUTestExpectationsParser::checkTokenCondition(const GPUTestConfig &config,
640                                                     bool &err,
641                                                     int32_t token,
642                                                     size_t lineNumber)
643 {
644     if (token >= kNumberOfTokens)
645     {
646         pushErrorMessage(kErrorMessage[kErrorIllegalEntry], lineNumber);
647         err = true;
648         return false;
649     }
650 
651     if (kTokenData[token].condition == GPUTestConfig::kConditionNone ||
652         kTokenData[token].condition >= GPUTestConfig::kNumberOfConditions)
653     {
654         pushErrorMessage(kErrorMessage[kErrorInvalidEntry], lineNumber);
655         // error on any unsupported conditions
656         err = true;
657         return false;
658     }
659     err = false;
660     return config.getConditions()[kTokenData[token].condition];
661 }
662 
detectConflictsBetweenEntries()663 bool GPUTestExpectationsParser::detectConflictsBetweenEntries()
664 {
665     bool rt = false;
666     for (size_t i = 0; i < mEntries.size(); ++i)
667     {
668         for (size_t j = i + 1; j < mEntries.size(); ++j)
669         {
670             const GPUTestExpectationEntry &entryI = mEntries[i];
671             const GPUTestExpectationEntry &entryJ = mEntries[j];
672             if (entryI.testName == entryJ.testName &&
673                 ConditionsOverlap(entryI.conditions, entryJ.conditions))
674             {
675                 pushErrorMessage(kErrorMessage[kErrorEntriesOverlap], entryI.lineNumber,
676                                  entryJ.lineNumber);
677                 rt = true;
678             }
679         }
680     }
681     return rt;
682 }
683 
684 std::vector<GPUTestExpectationsParser::GPUTestExpectationEntry>
getUnusedExpectations() const685 GPUTestExpectationsParser::getUnusedExpectations() const
686 {
687     std::vector<GPUTestExpectationsParser::GPUTestExpectationEntry> unusedExpectations;
688     for (size_t i = 0; i < mEntries.size(); ++i)
689     {
690         if (!mEntries[i].used)
691         {
692             unusedExpectations.push_back(mEntries[i]);
693         }
694     }
695     return unusedExpectations;
696 }
697 
pushErrorMessage(const std::string & message,size_t lineNumber)698 void GPUTestExpectationsParser::pushErrorMessage(const std::string &message, size_t lineNumber)
699 {
700     mErrorMessages.push_back("Line " + ToString(lineNumber) + " : " + message.c_str());
701 }
702 
pushErrorMessage(const std::string & message,size_t entry1LineNumber,size_t entry2LineNumber)703 void GPUTestExpectationsParser::pushErrorMessage(const std::string &message,
704                                                  size_t entry1LineNumber,
705                                                  size_t entry2LineNumber)
706 {
707     mErrorMessages.push_back("Line " + ToString(entry1LineNumber) + " and " +
708                              ToString(entry2LineNumber) + " : " + message.c_str());
709 }
710 
GPUTestExpectationEntry()711 GPUTestExpectationsParser::GPUTestExpectationEntry::GPUTestExpectationEntry()
712     : testExpectation(0), lineNumber(0)
713 {}
714 
715 }  // namespace angle
716