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