blob: bc14b72473c7d9b70b7c2a02af823aef48ccd58e [file] [log] [blame]
[email protected]db78bca52012-01-27 01:53:581// 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 "content/test/gpu/gpu_test_expectations_parser.h"
6
[email protected]4f3e6302012-02-08 02:44:397#include "base/base_paths.h"
[email protected]db78bca52012-01-27 01:53:588#include "base/file_util.h"
9#include "base/logging.h"
[email protected]4f3e6302012-02-08 02:44:3910#include "base/path_service.h"
[email protected]db78bca52012-01-27 01:53:5811#include "base/string_number_conversions.h"
12#include "base/string_split.h"
13#include "base/string_util.h"
14#include "base/stringprintf.h"
15
16namespace {
17
18enum LineParserStage {
19 kLineParserBegin = 0,
20 kLineParserBugID,
21 kLineParserConfigs,
22 kLineParserColon,
23 kLineParserTestName,
24 kLineParserEqual,
25 kLineParserExpectations,
26};
27
28enum Token {
29 // os
30 kConfigWinXP = 0,
31 kConfigWinVista,
32 kConfigWin7,
33 kConfigWin,
34 kConfigMacLeopard,
35 kConfigMacSnowLeopard,
36 kConfigMacLion,
37 kConfigMac,
38 kConfigLinux,
39 kConfigChromeOS,
40 // gpu vendor
41 kConfigNVidia,
42 kConfigAMD,
43 kConfigIntel,
[email protected]830631c2012-06-13 05:06:0844 kConfigVMWare,
[email protected]db78bca52012-01-27 01:53:5845 // build type
46 kConfigRelease,
47 kConfigDebug,
48 // expectation
49 kExpectationPass,
50 kExpectationFail,
51 kExpectationFlaky,
52 kExpectationTimeout,
[email protected]55ab0192012-05-11 20:12:3753 kExpectationSkip,
[email protected]db78bca52012-01-27 01:53:5854 // separator
55 kSeparatorColon,
56 kSeparatorEqual,
57
58 kNumberOfExactMatchTokens,
59
60 // others
61 kConfigGPUDeviceID,
62 kTokenComment,
63 kTokenWord,
64};
65
66struct TokenInfo {
67 const char* name;
68 int32 flag;
69};
70
71const TokenInfo kTokenData[] = {
72 { "xp", GPUTestConfig::kOsWinXP },
73 { "vista", GPUTestConfig::kOsWinVista },
74 { "win7", GPUTestConfig::kOsWin7 },
75 { "win", GPUTestConfig::kOsWin },
76 { "leopard", GPUTestConfig::kOsMacLeopard },
77 { "snowleopard", GPUTestConfig::kOsMacSnowLeopard },
78 { "lion", GPUTestConfig::kOsMacLion },
79 { "mac", GPUTestConfig::kOsMac },
80 { "linux", GPUTestConfig::kOsLinux },
81 { "chromeos", GPUTestConfig::kOsChromeOS },
82 { "nvidia", 0x10DE },
83 { "amd", 0x1002 },
84 { "intel", 0x8086 },
[email protected]830631c2012-06-13 05:06:0885 { "vmware", 0x15ad },
[email protected]db78bca52012-01-27 01:53:5886 { "release", GPUTestConfig::kBuildTypeRelease },
87 { "debug", GPUTestConfig::kBuildTypeDebug },
88 { "pass", GPUTestExpectationsParser::kGpuTestPass },
89 { "fail", GPUTestExpectationsParser::kGpuTestFail },
90 { "flaky", GPUTestExpectationsParser::kGpuTestFlaky },
91 { "timeout", GPUTestExpectationsParser::kGpuTestTimeout },
[email protected]55ab0192012-05-11 20:12:3792 { "skip", GPUTestExpectationsParser::kGpuTestSkip },
[email protected]db78bca52012-01-27 01:53:5893 { ":", 0 },
94 { "=", 0 },
95};
96
97enum ErrorType {
98 kErrorFileIO = 0,
99 kErrorIllegalEntry,
100 kErrorInvalidEntry,
101 kErrorEntryWithOsConflicts,
102 kErrorEntryWithGpuVendorConflicts,
103 kErrorEntryWithBuildTypeConflicts,
104 kErrorEntryWithGpuDeviceIdConflicts,
105 kErrorEntryWithExpectationConflicts,
106 kErrorEntriesOverlap,
107
108 kNumberOfErrors,
109};
110
111const char* kErrorMessage[] = {
112 "file IO failed",
113 "entry with wrong format",
114 "entry invalid, likely wrong modifiers combination",
115 "entry with OS modifier conflicts",
116 "entry with GPU vendor modifier conflicts",
117 "entry with GPU build type conflicts",
118 "entry with GPU device id conflicts or malformat",
119 "entry with expectation modifier conflicts",
120 "two entries's configs overlap",
121};
122
123Token ParseToken(const std::string& word) {
124 if (StartsWithASCII(word, "//", false))
125 return kTokenComment;
126 if (StartsWithASCII(word, "0x", false))
127 return kConfigGPUDeviceID;
128
129 for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) {
130 if (LowerCaseEqualsASCII(word, kTokenData[i].name))
131 return static_cast<Token>(i);
132 }
133 return kTokenWord;
134}
135
[email protected]55ab0192012-05-11 20:12:37136// reference name can have the last character as *.
137bool NamesMatching(const std::string& ref, const std::string& test_name) {
138 size_t len = ref.length();
139 if (len == 0)
140 return false;
141 if (ref[len - 1] == '*') {
142 if (test_name.length() > len -1 &&
143 ref.compare(0, len - 1, test_name, 0, len - 1) == 0)
144 return true;
145 return false;
146 }
147 return (ref == test_name);
148}
149
[email protected]db78bca52012-01-27 01:53:58150} // namespace anonymous
151
152GPUTestExpectationsParser::GPUTestExpectationsParser() {
153 // Some sanity check.
154 DCHECK_EQ(static_cast<unsigned int>(kNumberOfExactMatchTokens),
155 sizeof(kTokenData) / sizeof(kTokenData[0]));
156 DCHECK_EQ(static_cast<unsigned int>(kNumberOfErrors),
157 sizeof(kErrorMessage) / sizeof(kErrorMessage[0]));
158}
159
160GPUTestExpectationsParser::~GPUTestExpectationsParser() {
161}
162
163bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) {
164 entries_.clear();
165 error_messages_.clear();
166
167 std::vector<std::string> lines;
168 base::SplitString(data, '\n', &lines);
169 bool rt = true;
170 for (size_t i = 0; i < lines.size(); ++i) {
171 if (!ParseLine(lines[i], i + 1))
172 rt = false;
173 }
174 if (DetectConflictsBetweenEntries()) {
175 entries_.clear();
176 rt = false;
177 }
178
179 return rt;
180}
181
182bool GPUTestExpectationsParser::LoadTestExpectations(const FilePath& path) {
183 entries_.clear();
184 error_messages_.clear();
185
186 std::string data;
187 if (!file_util::ReadFileToString(path, &data)) {
188 error_messages_.push_back(kErrorMessage[kErrorFileIO]);
189 return false;
190 }
191 return LoadTestExpectations(data);
192}
193
[email protected]4f3e6302012-02-08 02:44:39194bool GPUTestExpectationsParser::LoadTestExpectations(
195 GPUTestProfile profile) {
196 FilePath path;
197 if (!GetExpectationsPath(profile, &path))
198 return false;
199 return LoadTestExpectations(path);
200}
201
[email protected]db78bca52012-01-27 01:53:58202int32 GPUTestExpectationsParser::GetTestExpectation(
203 const std::string& test_name,
204 const GPUTestBotConfig& bot_config) const {
205 for (size_t i = 0; i < entries_.size(); ++i) {
[email protected]55ab0192012-05-11 20:12:37206 if (NamesMatching(entries_[i].test_name, test_name) &&
[email protected]db78bca52012-01-27 01:53:58207 bot_config.Matches(entries_[i].test_config))
208 return entries_[i].test_expectation;
209 }
210 return kGpuTestPass;
211}
212
213const std::vector<std::string>&
214GPUTestExpectationsParser::GetErrorMessages() const {
215 return error_messages_;
216}
217
[email protected]830631c2012-06-13 05:06:08218bool GPUTestExpectationsParser::ParseConfig(
219 const std::string& config_data, GPUTestConfig* config) {
220 DCHECK(config);
221 std::vector<std::string> tokens;
222 base::SplitStringAlongWhitespace(config_data, &tokens);
223
224 for (size_t i = 0; i < tokens.size(); ++i) {
225 Token token = ParseToken(tokens[i]);
226 switch (token) {
227 case kConfigWinXP:
228 case kConfigWinVista:
229 case kConfigWin7:
230 case kConfigWin:
231 case kConfigMacLeopard:
232 case kConfigMacSnowLeopard:
233 case kConfigMacLion:
234 case kConfigMac:
235 case kConfigLinux:
236 case kConfigChromeOS:
237 case kConfigNVidia:
238 case kConfigAMD:
239 case kConfigIntel:
240 case kConfigVMWare:
241 case kConfigRelease:
242 case kConfigDebug:
243 case kConfigGPUDeviceID:
244 if (token == kConfigGPUDeviceID) {
245 if (!UpdateTestConfig(config, tokens[i], 0))
246 return false;
247 } else {
248 if (!UpdateTestConfig(config, token, 0))
249 return false;
250 }
251 break;
252 default:
253 return false;
254 }
255 }
256 return true;
257}
258
[email protected]db78bca52012-01-27 01:53:58259bool GPUTestExpectationsParser::ParseLine(
260 const std::string& line_data, size_t line_number) {
261 std::vector<std::string> tokens;
262 base::SplitStringAlongWhitespace(line_data, &tokens);
263 int32 stage = kLineParserBegin;
264 GPUTestExpectationEntry entry;
265 entry.line_number = line_number;
266 GPUTestConfig& config = entry.test_config;
267 bool comments_encountered = false;
268 for (size_t i = 0; i < tokens.size() && !comments_encountered; ++i) {
269 Token token = ParseToken(tokens[i]);
270 switch (token) {
271 case kTokenComment:
272 comments_encountered = true;
273 break;
274 case kConfigWinXP:
275 case kConfigWinVista:
276 case kConfigWin7:
277 case kConfigWin:
278 case kConfigMacLeopard:
279 case kConfigMacSnowLeopard:
280 case kConfigMacLion:
281 case kConfigMac:
282 case kConfigLinux:
283 case kConfigChromeOS:
284 case kConfigNVidia:
285 case kConfigAMD:
286 case kConfigIntel:
[email protected]830631c2012-06-13 05:06:08287 case kConfigVMWare:
[email protected]db78bca52012-01-27 01:53:58288 case kConfigRelease:
289 case kConfigDebug:
290 case kConfigGPUDeviceID:
291 // MODIFIERS, could be in any order, need at least one.
292 if (stage != kLineParserConfigs && stage != kLineParserBugID) {
293 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
294 line_number);
295 return false;
296 }
297 if (token == kConfigGPUDeviceID) {
298 if (!UpdateTestConfig(&config, tokens[i], line_number))
299 return false;
300 } else {
301 if (!UpdateTestConfig(&config, token, line_number))
302 return false;
303 }
304 if (stage == kLineParserBugID)
305 stage++;
306 break;
307 case kSeparatorColon:
308 // :
309 if (stage != kLineParserConfigs) {
310 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
311 line_number);
312 return false;
313 }
314 stage++;
315 break;
316 case kSeparatorEqual:
317 // =
318 if (stage != kLineParserTestName) {
319 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
320 line_number);
321 return false;
322 }
323 stage++;
324 break;
325 case kTokenWord:
326 // BUG_ID or TEST_NAME
327 if (stage == kLineParserBegin) {
328 // Bug ID is not used for anything; ignore it.
329 } else if (stage == kLineParserColon) {
330 entry.test_name = tokens[i];
331 } else {
332 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
333 line_number);
334 return false;
335 }
336 stage++;
337 break;
338 case kExpectationPass:
339 case kExpectationFail:
340 case kExpectationFlaky:
341 case kExpectationTimeout:
[email protected]55ab0192012-05-11 20:12:37342 case kExpectationSkip:
[email protected]db78bca52012-01-27 01:53:58343 // TEST_EXPECTATIONS
344 if (stage != kLineParserEqual && stage != kLineParserExpectations) {
345 PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
346 line_number);
347 return false;
348 }
349 if ((kTokenData[token].flag & entry.test_expectation) != 0) {
350 PushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
351 line_number);
352 return false;
353 }
354 entry.test_expectation =
355 (kTokenData[token].flag | entry.test_expectation);
356 if (stage == kLineParserEqual)
357 stage++;
358 break;
359 default:
360 DCHECK(false);
361 break;
362 }
363 }
364 if (stage == kLineParserBegin) {
365 // The whole line is empty or all comments
366 return true;
367 }
368 if (stage == kLineParserExpectations) {
369 if (!config.IsValid()) {
370 PushErrorMessage(kErrorMessage[kErrorInvalidEntry], line_number);
371 return false;
372 }
373 entries_.push_back(entry);
374 return true;
375 }
376 PushErrorMessage(kErrorMessage[kErrorIllegalEntry], line_number);
377 return false;
378}
379
380bool GPUTestExpectationsParser::UpdateTestConfig(
381 GPUTestConfig* config, int32 token, size_t line_number) {
382 DCHECK(config);
383 switch (token) {
384 case kConfigWinXP:
385 case kConfigWinVista:
386 case kConfigWin7:
387 case kConfigWin:
388 case kConfigMacLeopard:
389 case kConfigMacSnowLeopard:
390 case kConfigMacLion:
391 case kConfigMac:
392 case kConfigLinux:
393 case kConfigChromeOS:
394 if ((config->os() & kTokenData[token].flag) != 0) {
395 PushErrorMessage(kErrorMessage[kErrorEntryWithOsConflicts],
396 line_number);
397 return false;
398 }
399 config->set_os(config->os() | kTokenData[token].flag);
400 break;
401 case kConfigNVidia:
402 case kConfigAMD:
403 case kConfigIntel:
[email protected]830631c2012-06-13 05:06:08404 case kConfigVMWare:
[email protected]db78bca52012-01-27 01:53:58405 {
406 uint32 gpu_vendor =
407 static_cast<uint32>(kTokenData[token].flag);
408 for (size_t i = 0; i < config->gpu_vendor().size(); ++i) {
409 if (config->gpu_vendor()[i] == gpu_vendor) {
410 PushErrorMessage(
411 kErrorMessage[kErrorEntryWithGpuVendorConflicts],
412 line_number);
413 return false;
414 }
415 }
416 config->AddGPUVendor(gpu_vendor);
417 }
418 break;
419 case kConfigRelease:
420 case kConfigDebug:
421 if ((config->build_type() & kTokenData[token].flag) != 0) {
422 PushErrorMessage(
423 kErrorMessage[kErrorEntryWithBuildTypeConflicts],
424 line_number);
425 return false;
426 }
427 config->set_build_type(
428 config->build_type() | kTokenData[token].flag);
429 break;
430 default:
431 DCHECK(false);
432 break;
433 }
434 return true;
435}
436
437bool GPUTestExpectationsParser::UpdateTestConfig(
438 GPUTestConfig* config,
439 const std::string& gpu_device_id,
440 size_t line_number) {
441 DCHECK(config);
442 uint32 device_id = 0;
443 if (config->gpu_device_id() != 0 ||
444 !base::HexStringToInt(gpu_device_id,
445 reinterpret_cast<int*>(&device_id)) ||
446 device_id == 0) {
447 PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
448 line_number);
449 return false;
450 }
451 config->set_gpu_device_id(device_id);
452 return true;
453}
454
455bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
456 bool rt = false;
457 for (size_t i = 0; i < entries_.size(); ++i) {
458 for (size_t j = i + 1; j < entries_.size(); ++j) {
459 if (entries_[i].test_name == entries_[j].test_name &&
460 entries_[i].test_config.OverlapsWith(entries_[j].test_config)) {
461 PushErrorMessage(kErrorMessage[kErrorEntriesOverlap],
462 entries_[i].line_number,
463 entries_[j].line_number);
464 rt = true;
465 }
466 }
467 }
468 return rt;
469}
470
471void GPUTestExpectationsParser::PushErrorMessage(
472 const std::string& message, size_t line_number) {
473 error_messages_.push_back(
474 base::StringPrintf("Line %d : %s",
475 static_cast<int>(line_number), message.c_str()));
476}
477
478void GPUTestExpectationsParser::PushErrorMessage(
479 const std::string& message,
480 size_t entry1_line_number,
481 size_t entry2_line_number) {
482 error_messages_.push_back(
483 base::StringPrintf("Line %d and %d : %s",
484 static_cast<int>(entry1_line_number),
485 static_cast<int>(entry2_line_number),
486 message.c_str()));
487}
488
[email protected]4f3e6302012-02-08 02:44:39489// static
490bool GPUTestExpectationsParser::GetExpectationsPath(
491 GPUTestProfile profile, FilePath* path) {
492 DCHECK(path);
493
494 bool rt = true;
495 switch (profile) {
496 case kWebGLConformanceTest:
497 rt = PathService::Get(base::DIR_SOURCE_ROOT, path);
498 if (rt) {
499 *path = path->Append(FILE_PATH_LITERAL("chrome"))
500 .Append(FILE_PATH_LITERAL("test"))
501 .Append(FILE_PATH_LITERAL("gpu"))
502 .Append(FILE_PATH_LITERAL(
503 "webgl_conformance_test_expectations.txt"));
504 rt = file_util::PathExists(*path);
505 }
506 break;
507 default:
508 DCHECK(false);
509 }
510 return rt;
511}
512
[email protected]db78bca52012-01-27 01:53:58513GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
514 : test_expectation(0),
515 line_number(0) {
516}
517