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/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
,
61 kNumberOfExactMatchTokens
,
74 const TokenInfo kTokenData
[] = {
75 { "xp", GPUTestConfig::kOsWinXP
},
76 { "vista", GPUTestConfig::kOsWinVista
},
77 { "win7", GPUTestConfig::kOsWin7
},
78 { "win8", GPUTestConfig::kOsWin8
},
79 { "win", GPUTestConfig::kOsWin
},
80 { "leopard", GPUTestConfig::kOsMacLeopard
},
81 { "snowleopard", GPUTestConfig::kOsMacSnowLeopard
},
82 { "lion", GPUTestConfig::kOsMacLion
},
83 { "mountainlion", GPUTestConfig::kOsMacMountainLion
},
84 { "mac", GPUTestConfig::kOsMac
},
85 { "linux", GPUTestConfig::kOsLinux
},
86 { "chromeos", GPUTestConfig::kOsChromeOS
},
87 { "android", GPUTestConfig::kOsAndroid
},
92 { "release", GPUTestConfig::kBuildTypeRelease
},
93 { "debug", GPUTestConfig::kBuildTypeDebug
},
94 { "pass", GPUTestExpectationsParser::kGpuTestPass
},
95 { "fail", GPUTestExpectationsParser::kGpuTestFail
},
96 { "flaky", GPUTestExpectationsParser::kGpuTestFlaky
},
97 { "timeout", GPUTestExpectationsParser::kGpuTestTimeout
},
98 { "skip", GPUTestExpectationsParser::kGpuTestSkip
},
107 kErrorEntryWithOsConflicts
,
108 kErrorEntryWithGpuVendorConflicts
,
109 kErrorEntryWithBuildTypeConflicts
,
110 kErrorEntryWithGpuDeviceIdConflicts
,
111 kErrorEntryWithExpectationConflicts
,
112 kErrorEntriesOverlap
,
117 const char* kErrorMessage
[] = {
119 "entry with wrong format",
120 "entry invalid, likely wrong modifiers combination",
121 "entry with OS modifier conflicts",
122 "entry with GPU vendor modifier conflicts",
123 "entry with GPU build type conflicts",
124 "entry with GPU device id conflicts or malformat",
125 "entry with expectation modifier conflicts",
126 "two entries's configs overlap",
129 Token
ParseToken(const std::string
& word
) {
130 if (StartsWithASCII(word
, "//", false))
131 return kTokenComment
;
132 if (StartsWithASCII(word
, "0x", false))
133 return kConfigGPUDeviceID
;
135 for (int32 i
= 0; i
< kNumberOfExactMatchTokens
; ++i
) {
136 if (LowerCaseEqualsASCII(word
, kTokenData
[i
].name
))
137 return static_cast<Token
>(i
);
142 // reference name can have the last character as *.
143 bool NamesMatching(const std::string
& ref
, const std::string
& test_name
) {
144 size_t len
= ref
.length();
147 if (ref
[len
- 1] == '*') {
148 if (test_name
.length() > len
-1 &&
149 ref
.compare(0, len
- 1, test_name
, 0, len
- 1) == 0)
153 return (ref
== test_name
);
156 } // namespace anonymous
158 GPUTestExpectationsParser::GPUTestExpectationsParser() {
159 // Some sanity check.
160 DCHECK_EQ(static_cast<unsigned int>(kNumberOfExactMatchTokens
),
161 sizeof(kTokenData
) / sizeof(kTokenData
[0]));
162 DCHECK_EQ(static_cast<unsigned int>(kNumberOfErrors
),
163 sizeof(kErrorMessage
) / sizeof(kErrorMessage
[0]));
166 GPUTestExpectationsParser::~GPUTestExpectationsParser() {
169 bool GPUTestExpectationsParser::LoadTestExpectations(const std::string
& data
) {
171 error_messages_
.clear();
173 std::vector
<std::string
> lines
;
174 base::SplitString(data
, '\n', &lines
);
176 for (size_t i
= 0; i
< lines
.size(); ++i
) {
177 if (!ParseLine(lines
[i
], i
+ 1))
180 if (DetectConflictsBetweenEntries()) {
188 bool GPUTestExpectationsParser::LoadTestExpectations(
189 const base::FilePath
& path
) {
191 error_messages_
.clear();
194 if (!base::ReadFileToString(path
, &data
)) {
195 error_messages_
.push_back(kErrorMessage
[kErrorFileIO
]);
198 return LoadTestExpectations(data
);
201 int32
GPUTestExpectationsParser::GetTestExpectation(
202 const std::string
& test_name
,
203 const GPUTestBotConfig
& bot_config
) const {
204 for (size_t i
= 0; i
< entries_
.size(); ++i
) {
205 if (NamesMatching(entries_
[i
].test_name
, test_name
) &&
206 bot_config
.Matches(entries_
[i
].test_config
))
207 return entries_
[i
].test_expectation
;
212 const std::vector
<std::string
>&
213 GPUTestExpectationsParser::GetErrorMessages() const {
214 return error_messages_
;
217 bool GPUTestExpectationsParser::ParseConfig(
218 const std::string
& config_data
, GPUTestConfig
* config
) {
220 std::vector
<std::string
> tokens
;
221 base::SplitStringAlongWhitespace(config_data
, &tokens
);
223 for (size_t i
= 0; i
< tokens
.size(); ++i
) {
224 Token token
= ParseToken(tokens
[i
]);
227 case kConfigWinVista
:
231 case kConfigMacLeopard
:
232 case kConfigMacSnowLeopard
:
234 case kConfigMacMountainLion
:
237 case kConfigChromeOS
:
245 case kConfigGPUDeviceID
:
246 if (token
== kConfigGPUDeviceID
) {
247 if (!UpdateTestConfig(config
, tokens
[i
], 0))
250 if (!UpdateTestConfig(config
, token
, 0))
261 bool GPUTestExpectationsParser::ParseLine(
262 const std::string
& line_data
, size_t line_number
) {
263 std::vector
<std::string
> tokens
;
264 base::SplitStringAlongWhitespace(line_data
, &tokens
);
265 int32 stage
= kLineParserBegin
;
266 GPUTestExpectationEntry entry
;
267 entry
.line_number
= line_number
;
268 GPUTestConfig
& config
= entry
.test_config
;
269 bool comments_encountered
= false;
270 for (size_t i
= 0; i
< tokens
.size() && !comments_encountered
; ++i
) {
271 Token token
= ParseToken(tokens
[i
]);
274 comments_encountered
= true;
277 case kConfigWinVista
:
281 case kConfigMacLeopard
:
282 case kConfigMacSnowLeopard
:
284 case kConfigMacMountainLion
:
287 case kConfigChromeOS
:
295 case kConfigGPUDeviceID
:
296 // MODIFIERS, could be in any order, need at least one.
297 if (stage
!= kLineParserConfigs
&& stage
!= kLineParserBugID
) {
298 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
302 if (token
== kConfigGPUDeviceID
) {
303 if (!UpdateTestConfig(&config
, tokens
[i
], line_number
))
306 if (!UpdateTestConfig(&config
, token
, line_number
))
309 if (stage
== kLineParserBugID
)
312 case kSeparatorColon
:
314 if (stage
!= kLineParserConfigs
) {
315 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
321 case kSeparatorEqual
:
323 if (stage
!= kLineParserTestName
) {
324 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
331 // BUG_ID or TEST_NAME
332 if (stage
== kLineParserBegin
) {
333 // Bug ID is not used for anything; ignore it.
334 } else if (stage
== kLineParserColon
) {
335 entry
.test_name
= tokens
[i
];
337 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
343 case kExpectationPass
:
344 case kExpectationFail
:
345 case kExpectationFlaky
:
346 case kExpectationTimeout
:
347 case kExpectationSkip
:
349 if (stage
!= kLineParserEqual
&& stage
!= kLineParserExpectations
) {
350 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
354 if ((kTokenData
[token
].flag
& entry
.test_expectation
) != 0) {
355 PushErrorMessage(kErrorMessage
[kErrorEntryWithExpectationConflicts
],
359 entry
.test_expectation
=
360 (kTokenData
[token
].flag
| entry
.test_expectation
);
361 if (stage
== kLineParserEqual
)
369 if (stage
== kLineParserBegin
) {
370 // The whole line is empty or all comments
373 if (stage
== kLineParserExpectations
) {
374 if (!config
.IsValid()) {
375 PushErrorMessage(kErrorMessage
[kErrorInvalidEntry
], line_number
);
378 entries_
.push_back(entry
);
381 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
], line_number
);
385 bool GPUTestExpectationsParser::UpdateTestConfig(
386 GPUTestConfig
* config
, int32 token
, size_t line_number
) {
390 case kConfigWinVista
:
394 case kConfigMacLeopard
:
395 case kConfigMacSnowLeopard
:
397 case kConfigMacMountainLion
:
400 case kConfigChromeOS
:
402 if ((config
->os() & kTokenData
[token
].flag
) != 0) {
403 PushErrorMessage(kErrorMessage
[kErrorEntryWithOsConflicts
],
407 config
->set_os(config
->os() | kTokenData
[token
].flag
);
415 static_cast<uint32
>(kTokenData
[token
].flag
);
416 for (size_t i
= 0; i
< config
->gpu_vendor().size(); ++i
) {
417 if (config
->gpu_vendor()[i
] == gpu_vendor
) {
419 kErrorMessage
[kErrorEntryWithGpuVendorConflicts
],
424 config
->AddGPUVendor(gpu_vendor
);
429 if ((config
->build_type() & kTokenData
[token
].flag
) != 0) {
431 kErrorMessage
[kErrorEntryWithBuildTypeConflicts
],
435 config
->set_build_type(
436 config
->build_type() | kTokenData
[token
].flag
);
445 bool GPUTestExpectationsParser::UpdateTestConfig(
446 GPUTestConfig
* config
,
447 const std::string
& gpu_device_id
,
448 size_t line_number
) {
450 uint32 device_id
= 0;
451 if (config
->gpu_device_id() != 0 ||
452 !base::HexStringToInt(gpu_device_id
,
453 reinterpret_cast<int*>(&device_id
)) ||
455 PushErrorMessage(kErrorMessage
[kErrorEntryWithGpuDeviceIdConflicts
],
459 config
->set_gpu_device_id(device_id
);
463 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
465 for (size_t i
= 0; i
< entries_
.size(); ++i
) {
466 for (size_t j
= i
+ 1; j
< entries_
.size(); ++j
) {
467 if (entries_
[i
].test_name
== entries_
[j
].test_name
&&
468 entries_
[i
].test_config
.OverlapsWith(entries_
[j
].test_config
)) {
469 PushErrorMessage(kErrorMessage
[kErrorEntriesOverlap
],
470 entries_
[i
].line_number
,
471 entries_
[j
].line_number
);
479 void GPUTestExpectationsParser::PushErrorMessage(
480 const std::string
& message
, size_t line_number
) {
481 error_messages_
.push_back(
482 base::StringPrintf("Line %d : %s",
483 static_cast<int>(line_number
), message
.c_str()));
486 void GPUTestExpectationsParser::PushErrorMessage(
487 const std::string
& message
,
488 size_t entry1_line_number
,
489 size_t entry2_line_number
) {
490 error_messages_
.push_back(
491 base::StringPrintf("Line %d and %d : %s",
492 static_cast<int>(entry1_line_number
),
493 static_cast<int>(entry2_line_number
),
497 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
498 : test_expectation(0),