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