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"
18 enum LineParserStage
{
25 kLineParserExpectations
,
36 kConfigMacSnowLeopard
,
38 kConfigMacMountainLion
,
62 kNumberOfExactMatchTokens
,
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
},
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
},
109 kErrorEntryWithOsConflicts
,
110 kErrorEntryWithGpuVendorConflicts
,
111 kErrorEntryWithBuildTypeConflicts
,
112 kErrorEntryWithGpuDeviceIdConflicts
,
113 kErrorEntryWithExpectationConflicts
,
114 kErrorEntriesOverlap
,
119 const char* kErrorMessage
[] = {
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
);
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();
149 if (ref
[len
- 1] == '*') {
150 if (test_name
.length() > len
-1 &&
151 ref
.compare(0, len
- 1, test_name
, 0, len
- 1) == 0)
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
) {
173 error_messages_
.clear();
175 std::vector
<std::string
> lines
;
176 base::SplitString(data
, '\n', &lines
);
178 for (size_t i
= 0; i
< lines
.size(); ++i
) {
179 if (!ParseLine(lines
[i
], i
+ 1))
182 if (DetectConflictsBetweenEntries()) {
190 bool GPUTestExpectationsParser::LoadTestExpectations(
191 const base::FilePath
& path
) {
193 error_messages_
.clear();
196 if (!base::ReadFileToString(path
, &data
)) {
197 error_messages_
.push_back(kErrorMessage
[kErrorFileIO
]);
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
;
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
) {
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
]);
229 case kConfigWinVista
:
233 case kConfigMacLeopard
:
234 case kConfigMacSnowLeopard
:
236 case kConfigMacMountainLion
:
237 case kConfigMacMavericks
:
240 case kConfigChromeOS
:
248 case kConfigGPUDeviceID
:
249 if (token
== kConfigGPUDeviceID
) {
250 if (!UpdateTestConfig(config
, tokens
[i
], 0))
253 if (!UpdateTestConfig(config
, token
, 0))
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
]);
277 comments_encountered
= true;
280 case kConfigWinVista
:
284 case kConfigMacLeopard
:
285 case kConfigMacSnowLeopard
:
287 case kConfigMacMountainLion
:
288 case kConfigMacMavericks
:
291 case kConfigChromeOS
:
299 case kConfigGPUDeviceID
:
300 // MODIFIERS, could be in any order, need at least one.
301 if (stage
!= kLineParserConfigs
&& stage
!= kLineParserBugID
) {
302 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
306 if (token
== kConfigGPUDeviceID
) {
307 if (!UpdateTestConfig(&config
, tokens
[i
], line_number
))
310 if (!UpdateTestConfig(&config
, token
, line_number
))
313 if (stage
== kLineParserBugID
)
316 case kSeparatorColon
:
318 if (stage
!= kLineParserConfigs
) {
319 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
325 case kSeparatorEqual
:
327 if (stage
!= kLineParserTestName
) {
328 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
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
];
341 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
347 case kExpectationPass
:
348 case kExpectationFail
:
349 case kExpectationFlaky
:
350 case kExpectationTimeout
:
351 case kExpectationSkip
:
353 if (stage
!= kLineParserEqual
&& stage
!= kLineParserExpectations
) {
354 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
358 if ((kTokenData
[token
].flag
& entry
.test_expectation
) != 0) {
359 PushErrorMessage(kErrorMessage
[kErrorEntryWithExpectationConflicts
],
363 entry
.test_expectation
=
364 (kTokenData
[token
].flag
| entry
.test_expectation
);
365 if (stage
== kLineParserEqual
)
373 if (stage
== kLineParserBegin
) {
374 // The whole line is empty or all comments
377 if (stage
== kLineParserExpectations
) {
378 if (!config
.IsValid()) {
379 PushErrorMessage(kErrorMessage
[kErrorInvalidEntry
], line_number
);
382 entries_
.push_back(entry
);
385 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
], line_number
);
389 bool GPUTestExpectationsParser::UpdateTestConfig(
390 GPUTestConfig
* config
, int32 token
, size_t line_number
) {
394 case kConfigWinVista
:
398 case kConfigMacLeopard
:
399 case kConfigMacSnowLeopard
:
401 case kConfigMacMountainLion
:
402 case kConfigMacMavericks
:
405 case kConfigChromeOS
:
407 if ((config
->os() & kTokenData
[token
].flag
) != 0) {
408 PushErrorMessage(kErrorMessage
[kErrorEntryWithOsConflicts
],
412 config
->set_os(config
->os() | kTokenData
[token
].flag
);
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
) {
424 kErrorMessage
[kErrorEntryWithGpuVendorConflicts
],
429 config
->AddGPUVendor(gpu_vendor
);
434 if ((config
->build_type() & kTokenData
[token
].flag
) != 0) {
436 kErrorMessage
[kErrorEntryWithBuildTypeConflicts
],
440 config
->set_build_type(
441 config
->build_type() | kTokenData
[token
].flag
);
450 bool GPUTestExpectationsParser::UpdateTestConfig(
451 GPUTestConfig
* config
,
452 const std::string
& gpu_device_id
,
453 size_t line_number
) {
455 uint32 device_id
= 0;
456 if (config
->gpu_device_id() != 0 ||
457 !base::HexStringToUInt(gpu_device_id
, &device_id
) ||
459 PushErrorMessage(kErrorMessage
[kErrorEntryWithGpuDeviceIdConflicts
],
463 config
->set_gpu_device_id(device_id
);
467 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
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
);
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
),
501 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
502 : test_expectation(0),