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