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