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