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