Show Pages in chrome://md-settings
[chromium-blink-merge.git] / gpu / config / gpu_test_expectations_parser.cc
blob60baf189cfb68e99c3764ae06843a4af56b58f1d
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.
5 #include "gpu/config/gpu_test_expectations_parser.h"
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
14 namespace gpu {
16 namespace {
18 enum LineParserStage {
19 kLineParserBegin = 0,
20 kLineParserBugID,
21 kLineParserConfigs,
22 kLineParserColon,
23 kLineParserTestName,
24 kLineParserEqual,
25 kLineParserExpectations,
28 enum Token {
29 // os
30 kConfigWinXP = 0,
31 kConfigWinVista,
32 kConfigWin7,
33 kConfigWin8,
34 kConfigWin,
35 kConfigMacLeopard,
36 kConfigMacSnowLeopard,
37 kConfigMacLion,
38 kConfigMacMountainLion,
39 kConfigMacMavericks,
40 kConfigMac,
41 kConfigLinux,
42 kConfigChromeOS,
43 kConfigAndroid,
44 // gpu vendor
45 kConfigNVidia,
46 kConfigAMD,
47 kConfigIntel,
48 kConfigVMWare,
49 // build type
50 kConfigRelease,
51 kConfigDebug,
52 // expectation
53 kExpectationPass,
54 kExpectationFail,
55 kExpectationFlaky,
56 kExpectationTimeout,
57 kExpectationSkip,
58 // separator
59 kSeparatorColon,
60 kSeparatorEqual,
62 kNumberOfExactMatchTokens,
64 // others
65 kConfigGPUDeviceID,
66 kTokenComment,
67 kTokenWord,
70 struct TokenInfo {
71 const char* name;
72 int32 flag;
75 const TokenInfo kTokenData[] = {
76 { "xp", GPUTestConfig::kOsWinXP },
77 { "vista", GPUTestConfig::kOsWinVista },
78 { "win7", GPUTestConfig::kOsWin7 },
79 { "win8", GPUTestConfig::kOsWin8 },
80 { "win", GPUTestConfig::kOsWin },
81 { "leopard", GPUTestConfig::kOsMacLeopard },
82 { "snowleopard", GPUTestConfig::kOsMacSnowLeopard },
83 { "lion", GPUTestConfig::kOsMacLion },
84 { "mountainlion", GPUTestConfig::kOsMacMountainLion },
85 { "mavericks", GPUTestConfig::kOsMacMavericks },
86 { "mac", GPUTestConfig::kOsMac },
87 { "linux", GPUTestConfig::kOsLinux },
88 { "chromeos", GPUTestConfig::kOsChromeOS },
89 { "android", GPUTestConfig::kOsAndroid },
90 { "nvidia", 0x10DE },
91 { "amd", 0x1002 },
92 { "intel", 0x8086 },
93 { "vmware", 0x15ad },
94 { "release", GPUTestConfig::kBuildTypeRelease },
95 { "debug", GPUTestConfig::kBuildTypeDebug },
96 { "pass", GPUTestExpectationsParser::kGpuTestPass },
97 { "fail", GPUTestExpectationsParser::kGpuTestFail },
98 { "flaky", GPUTestExpectationsParser::kGpuTestFlaky },
99 { "timeout", GPUTestExpectationsParser::kGpuTestTimeout },
100 { "skip", GPUTestExpectationsParser::kGpuTestSkip },
101 { ":", 0 },
102 { "=", 0 },
105 enum ErrorType {
106 kErrorFileIO = 0,
107 kErrorIllegalEntry,
108 kErrorInvalidEntry,
109 kErrorEntryWithOsConflicts,
110 kErrorEntryWithGpuVendorConflicts,
111 kErrorEntryWithBuildTypeConflicts,
112 kErrorEntryWithGpuDeviceIdConflicts,
113 kErrorEntryWithExpectationConflicts,
114 kErrorEntriesOverlap,
116 kNumberOfErrors,
119 const char* kErrorMessage[] = {
120 "file IO failed",
121 "entry with wrong format",
122 "entry invalid, likely wrong modifiers combination",
123 "entry with OS modifier conflicts",
124 "entry with GPU vendor modifier conflicts",
125 "entry with GPU build type conflicts",
126 "entry with GPU device id conflicts or malformat",
127 "entry with expectation modifier conflicts",
128 "two entries's configs overlap",
131 Token ParseToken(const std::string& word) {
132 if (StartsWithASCII(word, "//", false))
133 return kTokenComment;
134 if (StartsWithASCII(word, "0x", false))
135 return kConfigGPUDeviceID;
137 for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) {
138 if (LowerCaseEqualsASCII(word, kTokenData[i].name))
139 return static_cast<Token>(i);
141 return kTokenWord;
144 // reference name can have the last character as *.
145 bool NamesMatching(const std::string& ref, const std::string& test_name) {
146 size_t len = ref.length();
147 if (len == 0)
148 return false;
149 if (ref[len - 1] == '*') {
150 if (test_name.length() > len -1 &&
151 ref.compare(0, len - 1, test_name, 0, len - 1) == 0)
152 return true;
153 return false;
155 return (ref == test_name);
158 } // namespace anonymous
160 GPUTestExpectationsParser::GPUTestExpectationsParser() {
161 // Some sanity check.
162 DCHECK_EQ(static_cast<unsigned int>(kNumberOfExactMatchTokens),
163 sizeof(kTokenData) / sizeof(kTokenData[0]));
164 DCHECK_EQ(static_cast<unsigned int>(kNumberOfErrors),
165 sizeof(kErrorMessage) / sizeof(kErrorMessage[0]));
168 GPUTestExpectationsParser::~GPUTestExpectationsParser() {
171 bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) {
172 entries_.clear();
173 error_messages_.clear();
175 std::vector<std::string> lines;
176 base::SplitString(data, '\n', &lines);
177 bool rt = true;
178 for (size_t i = 0; i < lines.size(); ++i) {
179 if (!ParseLine(lines[i], i + 1))
180 rt = false;
182 if (DetectConflictsBetweenEntries()) {
183 entries_.clear();
184 rt = false;
187 return rt;
190 bool GPUTestExpectationsParser::LoadTestExpectations(
191 const base::FilePath& path) {
192 entries_.clear();
193 error_messages_.clear();
195 std::string data;
196 if (!base::ReadFileToString(path, &data)) {
197 error_messages_.push_back(kErrorMessage[kErrorFileIO]);
198 return false;
200 return LoadTestExpectations(data);
203 int32 GPUTestExpectationsParser::GetTestExpectation(
204 const std::string& test_name,
205 const GPUTestBotConfig& bot_config) const {
206 for (size_t i = 0; i < entries_.size(); ++i) {
207 if (NamesMatching(entries_[i].test_name, test_name) &&
208 bot_config.Matches(entries_[i].test_config))
209 return entries_[i].test_expectation;
211 return kGpuTestPass;
214 const std::vector<std::string>&
215 GPUTestExpectationsParser::GetErrorMessages() const {
216 return error_messages_;
219 bool GPUTestExpectationsParser::ParseConfig(
220 const std::string& config_data, GPUTestConfig* config) {
221 DCHECK(config);
222 std::vector<std::string> tokens;
223 base::SplitStringAlongWhitespace(config_data, &tokens);
225 for (size_t i = 0; i < tokens.size(); ++i) {
226 Token token = ParseToken(tokens[i]);
227 switch (token) {
228 case kConfigWinXP:
229 case kConfigWinVista:
230 case kConfigWin7:
231 case kConfigWin8:
232 case kConfigWin:
233 case kConfigMacLeopard:
234 case kConfigMacSnowLeopard:
235 case kConfigMacLion:
236 case kConfigMacMountainLion:
237 case kConfigMacMavericks:
238 case kConfigMac:
239 case kConfigLinux:
240 case kConfigChromeOS:
241 case kConfigAndroid:
242 case kConfigNVidia:
243 case kConfigAMD:
244 case kConfigIntel:
245 case kConfigVMWare:
246 case kConfigRelease:
247 case kConfigDebug:
248 case kConfigGPUDeviceID:
249 if (token == kConfigGPUDeviceID) {
250 if (!UpdateTestConfig(config, tokens[i], 0))
251 return false;
252 } else {
253 if (!UpdateTestConfig(config, token, 0))
254 return false;
256 break;
257 default:
258 return false;
261 return true;
264 bool GPUTestExpectationsParser::ParseLine(
265 const std::string& line_data, size_t line_number) {
266 std::vector<std::string> tokens;
267 base::SplitStringAlongWhitespace(line_data, &tokens);
268 int32 stage = kLineParserBegin;
269 GPUTestExpectationEntry entry;
270 entry.line_number = line_number;
271 GPUTestConfig& config = entry.test_config;
272 bool comments_encountered = false;
273 for (size_t i = 0; i < tokens.size() && !comments_encountered; ++i) {
274 Token token = ParseToken(tokens[i]);
275 switch (token) {
276 case kTokenComment:
277 comments_encountered = true;
278 break;
279 case kConfigWinXP:
280 case kConfigWinVista:
281 case kConfigWin7:
282 case kConfigWin8:
283 case kConfigWin:
284 case kConfigMacLeopard:
285 case kConfigMacSnowLeopard:
286 case kConfigMacLion:
287 case kConfigMacMountainLion:
288 case kConfigMacMavericks:
289 case kConfigMac:
290 case kConfigLinux:
291 case kConfigChromeOS:
292 case kConfigAndroid:
293 case kConfigNVidia:
294 case kConfigAMD:
295 case kConfigIntel:
296 case kConfigVMWare:
297 case kConfigRelease:
298 case kConfigDebug:
299 case kConfigGPUDeviceID:
300 // MODIFIERS, could be in any order, need at least one.
301 if (stage != kLineParserConfigs && stage != kLineParserBugID) {
302 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
303 line_number);
304 return false;
306 if (token == kConfigGPUDeviceID) {
307 if (!UpdateTestConfig(&config, tokens[i], line_number))
308 return false;
309 } else {
310 if (!UpdateTestConfig(&config, token, line_number))
311 return false;
313 if (stage == kLineParserBugID)
314 stage++;
315 break;
316 case kSeparatorColon:
317 // :
318 if (stage != kLineParserConfigs) {
319 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
320 line_number);
321 return false;
323 stage++;
324 break;
325 case kSeparatorEqual:
326 // =
327 if (stage != kLineParserTestName) {
328 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
329 line_number);
330 return false;
332 stage++;
333 break;
334 case kTokenWord:
335 // BUG_ID or TEST_NAME
336 if (stage == kLineParserBegin) {
337 // Bug ID is not used for anything; ignore it.
338 } else if (stage == kLineParserColon) {
339 entry.test_name = tokens[i];
340 } else {
341 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
342 line_number);
343 return false;
345 stage++;
346 break;
347 case kExpectationPass:
348 case kExpectationFail:
349 case kExpectationFlaky:
350 case kExpectationTimeout:
351 case kExpectationSkip:
352 // TEST_EXPECTATIONS
353 if (stage != kLineParserEqual && stage != kLineParserExpectations) {
354 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
355 line_number);
356 return false;
358 if ((kTokenData[token].flag & entry.test_expectation) != 0) {
359 PushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
360 line_number);
361 return false;
363 entry.test_expectation =
364 (kTokenData[token].flag | entry.test_expectation);
365 if (stage == kLineParserEqual)
366 stage++;
367 break;
368 default:
369 DCHECK(false);
370 break;
373 if (stage == kLineParserBegin) {
374 // The whole line is empty or all comments
375 return true;
377 if (stage == kLineParserExpectations) {
378 if (!config.IsValid()) {
379 PushErrorMessage(kErrorMessage[kErrorInvalidEntry], line_number);
380 return false;
382 entries_.push_back(entry);
383 return true;
385 PushErrorMessage(kErrorMessage[kErrorIllegalEntry], line_number);
386 return false;
389 bool GPUTestExpectationsParser::UpdateTestConfig(
390 GPUTestConfig* config, int32 token, size_t line_number) {
391 DCHECK(config);
392 switch (token) {
393 case kConfigWinXP:
394 case kConfigWinVista:
395 case kConfigWin7:
396 case kConfigWin8:
397 case kConfigWin:
398 case kConfigMacLeopard:
399 case kConfigMacSnowLeopard:
400 case kConfigMacLion:
401 case kConfigMacMountainLion:
402 case kConfigMacMavericks:
403 case kConfigMac:
404 case kConfigLinux:
405 case kConfigChromeOS:
406 case kConfigAndroid:
407 if ((config->os() & kTokenData[token].flag) != 0) {
408 PushErrorMessage(kErrorMessage[kErrorEntryWithOsConflicts],
409 line_number);
410 return false;
412 config->set_os(config->os() | kTokenData[token].flag);
413 break;
414 case kConfigNVidia:
415 case kConfigAMD:
416 case kConfigIntel:
417 case kConfigVMWare:
419 uint32 gpu_vendor =
420 static_cast<uint32>(kTokenData[token].flag);
421 for (size_t i = 0; i < config->gpu_vendor().size(); ++i) {
422 if (config->gpu_vendor()[i] == gpu_vendor) {
423 PushErrorMessage(
424 kErrorMessage[kErrorEntryWithGpuVendorConflicts],
425 line_number);
426 return false;
429 config->AddGPUVendor(gpu_vendor);
431 break;
432 case kConfigRelease:
433 case kConfigDebug:
434 if ((config->build_type() & kTokenData[token].flag) != 0) {
435 PushErrorMessage(
436 kErrorMessage[kErrorEntryWithBuildTypeConflicts],
437 line_number);
438 return false;
440 config->set_build_type(
441 config->build_type() | kTokenData[token].flag);
442 break;
443 default:
444 DCHECK(false);
445 break;
447 return true;
450 bool GPUTestExpectationsParser::UpdateTestConfig(
451 GPUTestConfig* config,
452 const std::string& gpu_device_id,
453 size_t line_number) {
454 DCHECK(config);
455 uint32 device_id = 0;
456 if (config->gpu_device_id() != 0 ||
457 !base::HexStringToUInt(gpu_device_id, &device_id) ||
458 device_id == 0) {
459 PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
460 line_number);
461 return false;
463 config->set_gpu_device_id(device_id);
464 return true;
467 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
468 bool rt = false;
469 for (size_t i = 0; i < entries_.size(); ++i) {
470 for (size_t j = i + 1; j < entries_.size(); ++j) {
471 if (entries_[i].test_name == entries_[j].test_name &&
472 entries_[i].test_config.OverlapsWith(entries_[j].test_config)) {
473 PushErrorMessage(kErrorMessage[kErrorEntriesOverlap],
474 entries_[i].line_number,
475 entries_[j].line_number);
476 rt = true;
480 return rt;
483 void GPUTestExpectationsParser::PushErrorMessage(
484 const std::string& message, size_t line_number) {
485 error_messages_.push_back(
486 base::StringPrintf("Line %d : %s",
487 static_cast<int>(line_number), message.c_str()));
490 void GPUTestExpectationsParser::PushErrorMessage(
491 const std::string& message,
492 size_t entry1_line_number,
493 size_t entry2_line_number) {
494 error_messages_.push_back(
495 base::StringPrintf("Line %d and %d : %s",
496 static_cast<int>(entry1_line_number),
497 static_cast<int>(entry2_line_number),
498 message.c_str()));
501 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
502 : test_expectation(0),
503 line_number(0) {
506 } // namespace gpu