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