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