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