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