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