1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gpu/config/gpu_test_expectations_parser.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16
17 namespace gpu {
18
19 namespace {
20
21 enum LineParserStage {
22 kLineParserBegin = 0,
23 kLineParserBugID,
24 kLineParserConfigs,
25 kLineParserColon,
26 kLineParserTestName,
27 kLineParserEqual,
28 kLineParserExpectations,
29 };
30
31 enum Token {
32 // os
33 kConfigWinXP = 0,
34 kConfigWinVista,
35 kConfigWin7,
36 kConfigWin8,
37 kConfigWin10,
38 kConfigWin,
39 kConfigMacLeopard,
40 kConfigMacSnowLeopard,
41 kConfigMacLion,
42 kConfigMacMountainLion,
43 kConfigMacMavericks,
44 kConfigMacYosemite,
45 kConfigMacElCapitan,
46 kConfigMacSierra,
47 kConfigMacHighSierra,
48 kConfigMacMojave,
49 kConfigMacCatalina,
50 kConfigMac,
51 kConfigLinux,
52 kConfigChromeOS,
53 kConfigAndroid,
54 // gpu vendor
55 kConfigNVidia,
56 kConfigAMD,
57 kConfigIntel,
58 kConfigVMWare,
59 // build type
60 kConfigRelease,
61 kConfigDebug,
62 // ANGLE renderer
63 kConfigD3D9,
64 kConfigD3D11,
65 kConfigGLDesktop,
66 kConfigGLES,
67 // expectation
68 kExpectationPass,
69 kExpectationFail,
70 kExpectationFlaky,
71 kExpectationTimeout,
72 kExpectationSkip,
73 // separator
74 kSeparatorColon,
75 kSeparatorEqual,
76
77 kNumberOfExactMatchTokens,
78
79 // others
80 kConfigGPUDeviceID,
81 kTokenComment,
82 kTokenWord,
83 };
84
85 struct TokenInfo {
86 const char* name;
87 int32_t flag;
88 };
89
90 const TokenInfo kTokenData[] = {
91 {"xp", GPUTestConfig::kOsWinXP},
92 {"vista", GPUTestConfig::kOsWinVista},
93 {"win7", GPUTestConfig::kOsWin7},
94 {"win8", GPUTestConfig::kOsWin8},
95 {"win10", GPUTestConfig::kOsWin10},
96 {"win", GPUTestConfig::kOsWin},
97 {"leopard", GPUTestConfig::kOsMacLeopard},
98 {"snowleopard", GPUTestConfig::kOsMacSnowLeopard},
99 {"lion", GPUTestConfig::kOsMacLion},
100 {"mountainlion", GPUTestConfig::kOsMacMountainLion},
101 {"mavericks", GPUTestConfig::kOsMacMavericks},
102 {"yosemite", GPUTestConfig::kOsMacYosemite},
103 {"elcapitan", GPUTestConfig::kOsMacElCapitan},
104 {"sierra", GPUTestConfig::kOsMacSierra},
105 {"highsierra", GPUTestConfig::kOsMacHighSierra},
106 {"mojave", GPUTestConfig::kOsMacMojave},
107 {"catalina", GPUTestConfig::kOsMacCatalina},
108 {"mac", GPUTestConfig::kOsMac},
109 {"linux", GPUTestConfig::kOsLinux},
110 {"chromeos", GPUTestConfig::kOsChromeOS},
111 {"android", GPUTestConfig::kOsAndroid},
112 {"nvidia", 0x10DE},
113 {"amd", 0x1002},
114 {"intel", 0x8086},
115 {"vmware", 0x15ad},
116 {"release", GPUTestConfig::kBuildTypeRelease},
117 {"debug", GPUTestConfig::kBuildTypeDebug},
118 {"d3d9", GPUTestConfig::kAPID3D9},
119 {"d3d11", GPUTestConfig::kAPID3D11},
120 {"opengl", GPUTestConfig::kAPIGLDesktop},
121 {"gles", GPUTestConfig::kAPIGLES},
122 {"pass", GPUTestExpectationsParser::kGpuTestPass},
123 {"fail", GPUTestExpectationsParser::kGpuTestFail},
124 {"flaky", GPUTestExpectationsParser::kGpuTestFlaky},
125 {"timeout", GPUTestExpectationsParser::kGpuTestTimeout},
126 {"skip", GPUTestExpectationsParser::kGpuTestSkip},
127 {":", 0},
128 {"=", 0},
129 };
130
131 enum ErrorType {
132 kErrorFileIO = 0,
133 kErrorIllegalEntry,
134 kErrorInvalidEntry,
135 kErrorEntryWithOsConflicts,
136 kErrorEntryWithGpuVendorConflicts,
137 kErrorEntryWithBuildTypeConflicts,
138 kErrorEntryWithAPIConflicts,
139 kErrorEntryWithGpuDeviceIdConflicts,
140 kErrorEntryWithExpectationConflicts,
141 kErrorEntriesOverlap,
142
143 kNumberOfErrors,
144 };
145
146 const char* kErrorMessage[] = {
147 "file IO failed",
148 "entry with wrong format",
149 "entry invalid, likely wrong modifiers combination",
150 "entry with OS modifier conflicts",
151 "entry with GPU vendor modifier conflicts",
152 "entry with GPU build type conflicts",
153 "entry with GPU API conflicts",
154 "entry with GPU device id conflicts or malformat",
155 "entry with expectation modifier conflicts",
156 "two entries' configs overlap",
157 };
158
ParseToken(const std::string & word)159 Token ParseToken(const std::string& word) {
160 if (base::StartsWith(word, "//", base::CompareCase::INSENSITIVE_ASCII))
161 return kTokenComment;
162 if (base::StartsWith(word, "0x", base::CompareCase::INSENSITIVE_ASCII))
163 return kConfigGPUDeviceID;
164
165 for (int32_t i = 0; i < kNumberOfExactMatchTokens; ++i) {
166 if (base::LowerCaseEqualsASCII(word, kTokenData[i].name))
167 return static_cast<Token>(i);
168 }
169 return kTokenWord;
170 }
171
172 // reference name can have the last character as *.
NamesMatching(const std::string & ref,const std::string & test_name)173 bool NamesMatching(const std::string& ref, const std::string& test_name) {
174 size_t len = ref.length();
175 if (len == 0)
176 return false;
177 if (ref[len - 1] == '*') {
178 if (test_name.length() > len -1 &&
179 ref.compare(0, len - 1, test_name, 0, len - 1) == 0)
180 return true;
181 return false;
182 }
183 return (ref == test_name);
184 }
185
186 } // namespace anonymous
187
GPUTestExpectationsParser()188 GPUTestExpectationsParser::GPUTestExpectationsParser() {
189 // Some sanity check.
190 DCHECK_EQ(static_cast<unsigned int>(kNumberOfExactMatchTokens),
191 sizeof(kTokenData) / sizeof(kTokenData[0]));
192 DCHECK_EQ(static_cast<unsigned int>(kNumberOfErrors),
193 sizeof(kErrorMessage) / sizeof(kErrorMessage[0]));
194 }
195
196 GPUTestExpectationsParser::~GPUTestExpectationsParser() = default;
197
LoadTestExpectations(const std::string & data)198 bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) {
199 entries_.clear();
200 error_messages_.clear();
201
202 std::vector<std::string> lines = base::SplitString(
203 data, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
204 bool rt = true;
205 for (size_t i = 0; i < lines.size(); ++i) {
206 if (!ParseLine(lines[i], i + 1))
207 rt = false;
208 }
209 if (DetectConflictsBetweenEntries()) {
210 entries_.clear();
211 rt = false;
212 }
213
214 return rt;
215 }
216
LoadTestExpectations(const base::FilePath & path)217 bool GPUTestExpectationsParser::LoadTestExpectations(
218 const base::FilePath& path) {
219 entries_.clear();
220 error_messages_.clear();
221
222 std::string data;
223 if (!base::ReadFileToString(path, &data)) {
224 error_messages_.push_back(kErrorMessage[kErrorFileIO]);
225 return false;
226 }
227 return LoadTestExpectations(data);
228 }
229
GetTestExpectation(const std::string & test_name,const GPUTestBotConfig & bot_config) const230 int32_t GPUTestExpectationsParser::GetTestExpectation(
231 const std::string& test_name,
232 const GPUTestBotConfig& bot_config) const {
233 for (size_t i = 0; i < entries_.size(); ++i) {
234 if (NamesMatching(entries_[i].test_name, test_name) &&
235 bot_config.Matches(entries_[i].test_config))
236 return entries_[i].test_expectation;
237 }
238 return kGpuTestPass;
239 }
240
241 const std::vector<std::string>&
GetErrorMessages() const242 GPUTestExpectationsParser::GetErrorMessages() const {
243 return error_messages_;
244 }
245
ParseConfig(const std::string & config_data,GPUTestConfig * config)246 bool GPUTestExpectationsParser::ParseConfig(
247 const std::string& config_data, GPUTestConfig* config) {
248 DCHECK(config);
249 std::vector<std::string> tokens = base::SplitString(
250 config_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE,
251 base::SPLIT_WANT_NONEMPTY);
252
253 for (size_t i = 0; i < tokens.size(); ++i) {
254 Token token = ParseToken(tokens[i]);
255 switch (token) {
256 case kConfigWinXP:
257 case kConfigWinVista:
258 case kConfigWin7:
259 case kConfigWin8:
260 case kConfigWin10:
261 case kConfigWin:
262 case kConfigMacLeopard:
263 case kConfigMacSnowLeopard:
264 case kConfigMacLion:
265 case kConfigMacMountainLion:
266 case kConfigMacMavericks:
267 case kConfigMacYosemite:
268 case kConfigMacElCapitan:
269 case kConfigMacSierra:
270 case kConfigMacHighSierra:
271 case kConfigMacMojave:
272 case kConfigMacCatalina:
273 case kConfigMac:
274 case kConfigLinux:
275 case kConfigChromeOS:
276 case kConfigAndroid:
277 case kConfigNVidia:
278 case kConfigAMD:
279 case kConfigIntel:
280 case kConfigVMWare:
281 case kConfigRelease:
282 case kConfigDebug:
283 case kConfigD3D9:
284 case kConfigD3D11:
285 case kConfigGLDesktop:
286 case kConfigGLES:
287 case kConfigGPUDeviceID:
288 if (token == kConfigGPUDeviceID) {
289 if (!UpdateTestConfig(config, tokens[i], 0))
290 return false;
291 } else {
292 if (!UpdateTestConfig(config, token, 0))
293 return false;
294 }
295 break;
296 default:
297 return false;
298 }
299 }
300 return true;
301 }
302
ParseLine(const std::string & line_data,size_t line_number)303 bool GPUTestExpectationsParser::ParseLine(
304 const std::string& line_data, size_t line_number) {
305 std::vector<std::string> tokens = base::SplitString(
306 line_data, base::kWhitespaceASCII, base::KEEP_WHITESPACE,
307 base::SPLIT_WANT_NONEMPTY);
308 int32_t stage = kLineParserBegin;
309 GPUTestExpectationEntry entry;
310 entry.line_number = line_number;
311 GPUTestConfig& config = entry.test_config;
312 bool comments_encountered = false;
313 for (size_t i = 0; i < tokens.size() && !comments_encountered; ++i) {
314 Token token = ParseToken(tokens[i]);
315 switch (token) {
316 case kTokenComment:
317 comments_encountered = true;
318 break;
319 case kConfigWinXP:
320 case kConfigWinVista:
321 case kConfigWin7:
322 case kConfigWin8:
323 case kConfigWin10:
324 case kConfigWin:
325 case kConfigMacLeopard:
326 case kConfigMacSnowLeopard:
327 case kConfigMacLion:
328 case kConfigMacMountainLion:
329 case kConfigMacMavericks:
330 case kConfigMacYosemite:
331 case kConfigMacElCapitan:
332 case kConfigMacSierra:
333 case kConfigMacHighSierra:
334 case kConfigMacMojave:
335 case kConfigMacCatalina:
336 case kConfigMac:
337 case kConfigLinux:
338 case kConfigChromeOS:
339 case kConfigAndroid:
340 case kConfigNVidia:
341 case kConfigAMD:
342 case kConfigIntel:
343 case kConfigVMWare:
344 case kConfigRelease:
345 case kConfigDebug:
346 case kConfigD3D9:
347 case kConfigD3D11:
348 case kConfigGLDesktop:
349 case kConfigGLES:
350 case kConfigGPUDeviceID:
351 // MODIFIERS, could be in any order, need at least one.
352 if (stage != kLineParserConfigs && stage != kLineParserBugID) {
353 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
354 line_number);
355 return false;
356 }
357 if (token == kConfigGPUDeviceID) {
358 if (!UpdateTestConfig(&config, tokens[i], line_number))
359 return false;
360 } else {
361 if (!UpdateTestConfig(&config, token, line_number))
362 return false;
363 }
364 if (stage == kLineParserBugID)
365 stage++;
366 break;
367 case kSeparatorColon:
368 // :
369 if (stage != kLineParserConfigs) {
370 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
371 line_number);
372 return false;
373 }
374 stage++;
375 break;
376 case kSeparatorEqual:
377 // =
378 if (stage != kLineParserTestName) {
379 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
380 line_number);
381 return false;
382 }
383 stage++;
384 break;
385 case kTokenWord:
386 // BUG_ID or TEST_NAME
387 if (stage == kLineParserBegin) {
388 // Bug ID is not used for anything; ignore it.
389 } else if (stage == kLineParserColon) {
390 entry.test_name = tokens[i];
391 } else {
392 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
393 line_number);
394 return false;
395 }
396 stage++;
397 break;
398 case kExpectationPass:
399 case kExpectationFail:
400 case kExpectationFlaky:
401 case kExpectationTimeout:
402 case kExpectationSkip:
403 // TEST_EXPECTATIONS
404 if (stage != kLineParserEqual && stage != kLineParserExpectations) {
405 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
406 line_number);
407 return false;
408 }
409 if ((kTokenData[token].flag & entry.test_expectation) != 0) {
410 PushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
411 line_number);
412 return false;
413 }
414 entry.test_expectation =
415 (kTokenData[token].flag | entry.test_expectation);
416 if (stage == kLineParserEqual)
417 stage++;
418 break;
419 default:
420 DCHECK(false);
421 break;
422 }
423 }
424 if (stage == kLineParserBegin) {
425 // The whole line is empty or all comments
426 return true;
427 }
428 if (stage == kLineParserExpectations) {
429 if (!config.IsValid()) {
430 PushErrorMessage(kErrorMessage[kErrorInvalidEntry], line_number);
431 return false;
432 }
433 entries_.push_back(entry);
434 return true;
435 }
436 PushErrorMessage(kErrorMessage[kErrorIllegalEntry], line_number);
437 return false;
438 }
439
UpdateTestConfig(GPUTestConfig * config,int32_t token,size_t line_number)440 bool GPUTestExpectationsParser::UpdateTestConfig(GPUTestConfig* config,
441 int32_t token,
442 size_t line_number) {
443 DCHECK(config);
444 switch (token) {
445 case kConfigWinXP:
446 case kConfigWinVista:
447 case kConfigWin7:
448 case kConfigWin8:
449 case kConfigWin10:
450 case kConfigWin:
451 case kConfigMacLeopard:
452 case kConfigMacSnowLeopard:
453 case kConfigMacLion:
454 case kConfigMacMountainLion:
455 case kConfigMacMavericks:
456 case kConfigMacYosemite:
457 case kConfigMacElCapitan:
458 case kConfigMacSierra:
459 case kConfigMacHighSierra:
460 case kConfigMacMojave:
461 case kConfigMacCatalina:
462 case kConfigMac:
463 case kConfigLinux:
464 case kConfigChromeOS:
465 case kConfigAndroid:
466 if ((config->os() & kTokenData[token].flag) != 0) {
467 PushErrorMessage(kErrorMessage[kErrorEntryWithOsConflicts],
468 line_number);
469 return false;
470 }
471 config->set_os(config->os() | kTokenData[token].flag);
472 break;
473 case kConfigNVidia:
474 case kConfigAMD:
475 case kConfigIntel:
476 case kConfigVMWare:
477 {
478 uint32_t gpu_vendor = static_cast<uint32_t>(kTokenData[token].flag);
479 for (size_t i = 0; i < config->gpu_vendor().size(); ++i) {
480 if (config->gpu_vendor()[i] == gpu_vendor) {
481 PushErrorMessage(
482 kErrorMessage[kErrorEntryWithGpuVendorConflicts],
483 line_number);
484 return false;
485 }
486 }
487 config->AddGPUVendor(gpu_vendor);
488 }
489 break;
490 case kConfigRelease:
491 case kConfigDebug:
492 if ((config->build_type() & kTokenData[token].flag) != 0) {
493 PushErrorMessage(
494 kErrorMessage[kErrorEntryWithBuildTypeConflicts],
495 line_number);
496 return false;
497 }
498 config->set_build_type(
499 config->build_type() | kTokenData[token].flag);
500 break;
501 case kConfigD3D9:
502 case kConfigD3D11:
503 case kConfigGLDesktop:
504 case kConfigGLES:
505 if ((config->api() & kTokenData[token].flag) != 0) {
506 PushErrorMessage(kErrorMessage[kErrorEntryWithAPIConflicts],
507 line_number);
508 return false;
509 }
510 config->set_api(config->api() | kTokenData[token].flag);
511 break;
512 default:
513 DCHECK(false);
514 break;
515 }
516 return true;
517 }
518
UpdateTestConfig(GPUTestConfig * config,const std::string & gpu_device_id,size_t line_number)519 bool GPUTestExpectationsParser::UpdateTestConfig(
520 GPUTestConfig* config,
521 const std::string& gpu_device_id,
522 size_t line_number) {
523 DCHECK(config);
524 uint32_t device_id = 0;
525 if (config->gpu_device_id() != 0 ||
526 !base::HexStringToUInt(gpu_device_id, &device_id) ||
527 device_id == 0) {
528 PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
529 line_number);
530 return false;
531 }
532 config->set_gpu_device_id(device_id);
533 return true;
534 }
535
DetectConflictsBetweenEntries()536 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
537 bool rt = false;
538 for (size_t i = 0; i < entries_.size(); ++i) {
539 for (size_t j = i + 1; j < entries_.size(); ++j) {
540 if (entries_[i].test_name == entries_[j].test_name &&
541 entries_[i].test_config.OverlapsWith(entries_[j].test_config)) {
542 PushErrorMessage(kErrorMessage[kErrorEntriesOverlap],
543 entries_[i].line_number,
544 entries_[j].line_number);
545 rt = true;
546 }
547 }
548 }
549 return rt;
550 }
551
PushErrorMessage(const std::string & message,size_t line_number)552 void GPUTestExpectationsParser::PushErrorMessage(
553 const std::string& message, size_t line_number) {
554 error_messages_.push_back(
555 base::StringPrintf("Line %d : %s",
556 static_cast<int>(line_number), message.c_str()));
557 }
558
PushErrorMessage(const std::string & message,size_t entry1_line_number,size_t entry2_line_number)559 void GPUTestExpectationsParser::PushErrorMessage(
560 const std::string& message,
561 size_t entry1_line_number,
562 size_t entry2_line_number) {
563 error_messages_.push_back(
564 base::StringPrintf("Line %d and %d : %s",
565 static_cast<int>(entry1_line_number),
566 static_cast<int>(entry2_line_number),
567 message.c_str()));
568 }
569
GPUTestExpectationEntry()570 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
571 : test_expectation(0),
572 line_number(0) {
573 }
574
575 } // namespace gpu
576