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