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
,
37 kConfigMacSnowLeopard
,
39 kConfigMacMountainLion
,
69 kNumberOfExactMatchTokens
,
82 const TokenInfo kTokenData
[] = {
83 {"xp", GPUTestConfig::kOsWinXP
},
84 {"vista", GPUTestConfig::kOsWinVista
},
85 {"win7", GPUTestConfig::kOsWin7
},
86 {"win8", GPUTestConfig::kOsWin8
},
87 {"win10", GPUTestConfig::kOsWin10
},
88 {"win", GPUTestConfig::kOsWin
},
89 {"leopard", GPUTestConfig::kOsMacLeopard
},
90 {"snowleopard", GPUTestConfig::kOsMacSnowLeopard
},
91 {"lion", GPUTestConfig::kOsMacLion
},
92 {"mountainlion", GPUTestConfig::kOsMacMountainLion
},
93 {"mavericks", GPUTestConfig::kOsMacMavericks
},
94 {"yosemite", GPUTestConfig::kOsMacYosemite
},
95 {"mac", GPUTestConfig::kOsMac
},
96 {"linux", GPUTestConfig::kOsLinux
},
97 {"chromeos", GPUTestConfig::kOsChromeOS
},
98 {"android", GPUTestConfig::kOsAndroid
},
103 {"release", GPUTestConfig::kBuildTypeRelease
},
104 {"debug", GPUTestConfig::kBuildTypeDebug
},
105 {"d3d9", GPUTestConfig::kAPID3D9
},
106 {"d3d11", GPUTestConfig::kAPID3D11
},
107 {"opengl", GPUTestConfig::kAPIGLDesktop
},
108 {"gles", GPUTestConfig::kAPIGLES
},
109 {"pass", GPUTestExpectationsParser::kGpuTestPass
},
110 {"fail", GPUTestExpectationsParser::kGpuTestFail
},
111 {"flaky", GPUTestExpectationsParser::kGpuTestFlaky
},
112 {"timeout", GPUTestExpectationsParser::kGpuTestTimeout
},
113 {"skip", GPUTestExpectationsParser::kGpuTestSkip
},
122 kErrorEntryWithOsConflicts
,
123 kErrorEntryWithGpuVendorConflicts
,
124 kErrorEntryWithBuildTypeConflicts
,
125 kErrorEntryWithAPIConflicts
,
126 kErrorEntryWithGpuDeviceIdConflicts
,
127 kErrorEntryWithExpectationConflicts
,
128 kErrorEntriesOverlap
,
133 const char* kErrorMessage
[] = {
135 "entry with wrong format",
136 "entry invalid, likely wrong modifiers combination",
137 "entry with OS modifier conflicts",
138 "entry with GPU vendor modifier conflicts",
139 "entry with GPU build type conflicts",
140 "entry with GPU API conflicts",
141 "entry with GPU device id conflicts or malformat",
142 "entry with expectation modifier conflicts",
143 "two entries' configs overlap",
146 Token
ParseToken(const std::string
& word
) {
147 if (base::StartsWith(word
, "//", base::CompareCase::INSENSITIVE_ASCII
))
148 return kTokenComment
;
149 if (base::StartsWith(word
, "0x", base::CompareCase::INSENSITIVE_ASCII
))
150 return kConfigGPUDeviceID
;
152 for (int32 i
= 0; i
< kNumberOfExactMatchTokens
; ++i
) {
153 if (base::LowerCaseEqualsASCII(word
, kTokenData
[i
].name
))
154 return static_cast<Token
>(i
);
159 // reference name can have the last character as *.
160 bool NamesMatching(const std::string
& ref
, const std::string
& test_name
) {
161 size_t len
= ref
.length();
164 if (ref
[len
- 1] == '*') {
165 if (test_name
.length() > len
-1 &&
166 ref
.compare(0, len
- 1, test_name
, 0, len
- 1) == 0)
170 return (ref
== test_name
);
173 } // namespace anonymous
175 GPUTestExpectationsParser::GPUTestExpectationsParser() {
176 // Some sanity check.
177 DCHECK_EQ(static_cast<unsigned int>(kNumberOfExactMatchTokens
),
178 sizeof(kTokenData
) / sizeof(kTokenData
[0]));
179 DCHECK_EQ(static_cast<unsigned int>(kNumberOfErrors
),
180 sizeof(kErrorMessage
) / sizeof(kErrorMessage
[0]));
183 GPUTestExpectationsParser::~GPUTestExpectationsParser() {
186 bool GPUTestExpectationsParser::LoadTestExpectations(const std::string
& data
) {
188 error_messages_
.clear();
190 std::vector
<std::string
> lines
= base::SplitString(
191 data
, "\n", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
193 for (size_t i
= 0; i
< lines
.size(); ++i
) {
194 if (!ParseLine(lines
[i
], i
+ 1))
197 if (DetectConflictsBetweenEntries()) {
205 bool GPUTestExpectationsParser::LoadTestExpectations(
206 const base::FilePath
& path
) {
208 error_messages_
.clear();
211 if (!base::ReadFileToString(path
, &data
)) {
212 error_messages_
.push_back(kErrorMessage
[kErrorFileIO
]);
215 return LoadTestExpectations(data
);
218 int32
GPUTestExpectationsParser::GetTestExpectation(
219 const std::string
& test_name
,
220 const GPUTestBotConfig
& bot_config
) const {
221 for (size_t i
= 0; i
< entries_
.size(); ++i
) {
222 if (NamesMatching(entries_
[i
].test_name
, test_name
) &&
223 bot_config
.Matches(entries_
[i
].test_config
))
224 return entries_
[i
].test_expectation
;
229 const std::vector
<std::string
>&
230 GPUTestExpectationsParser::GetErrorMessages() const {
231 return error_messages_
;
234 bool GPUTestExpectationsParser::ParseConfig(
235 const std::string
& config_data
, GPUTestConfig
* config
) {
237 std::vector
<std::string
> tokens
= base::SplitString(
238 config_data
, base::kWhitespaceASCII
, base::KEEP_WHITESPACE
,
239 base::SPLIT_WANT_NONEMPTY
);
241 for (size_t i
= 0; i
< tokens
.size(); ++i
) {
242 Token token
= ParseToken(tokens
[i
]);
245 case kConfigWinVista
:
250 case kConfigMacLeopard
:
251 case kConfigMacSnowLeopard
:
253 case kConfigMacMountainLion
:
254 case kConfigMacMavericks
:
255 case kConfigMacYosemite
:
258 case kConfigChromeOS
:
268 case kConfigGLDesktop
:
270 case kConfigGPUDeviceID
:
271 if (token
== kConfigGPUDeviceID
) {
272 if (!UpdateTestConfig(config
, tokens
[i
], 0))
275 if (!UpdateTestConfig(config
, token
, 0))
286 bool GPUTestExpectationsParser::ParseLine(
287 const std::string
& line_data
, size_t line_number
) {
288 std::vector
<std::string
> tokens
= base::SplitString(
289 line_data
, base::kWhitespaceASCII
, base::KEEP_WHITESPACE
,
290 base::SPLIT_WANT_NONEMPTY
);
291 int32 stage
= kLineParserBegin
;
292 GPUTestExpectationEntry entry
;
293 entry
.line_number
= line_number
;
294 GPUTestConfig
& config
= entry
.test_config
;
295 bool comments_encountered
= false;
296 for (size_t i
= 0; i
< tokens
.size() && !comments_encountered
; ++i
) {
297 Token token
= ParseToken(tokens
[i
]);
300 comments_encountered
= true;
303 case kConfigWinVista
:
308 case kConfigMacLeopard
:
309 case kConfigMacSnowLeopard
:
311 case kConfigMacMountainLion
:
312 case kConfigMacMavericks
:
313 case kConfigMacYosemite
:
316 case kConfigChromeOS
:
326 case kConfigGLDesktop
:
328 case kConfigGPUDeviceID
:
329 // MODIFIERS, could be in any order, need at least one.
330 if (stage
!= kLineParserConfigs
&& stage
!= kLineParserBugID
) {
331 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
335 if (token
== kConfigGPUDeviceID
) {
336 if (!UpdateTestConfig(&config
, tokens
[i
], line_number
))
339 if (!UpdateTestConfig(&config
, token
, line_number
))
342 if (stage
== kLineParserBugID
)
345 case kSeparatorColon
:
347 if (stage
!= kLineParserConfigs
) {
348 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
354 case kSeparatorEqual
:
356 if (stage
!= kLineParserTestName
) {
357 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
364 // BUG_ID or TEST_NAME
365 if (stage
== kLineParserBegin
) {
366 // Bug ID is not used for anything; ignore it.
367 } else if (stage
== kLineParserColon
) {
368 entry
.test_name
= tokens
[i
];
370 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
376 case kExpectationPass
:
377 case kExpectationFail
:
378 case kExpectationFlaky
:
379 case kExpectationTimeout
:
380 case kExpectationSkip
:
382 if (stage
!= kLineParserEqual
&& stage
!= kLineParserExpectations
) {
383 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
],
387 if ((kTokenData
[token
].flag
& entry
.test_expectation
) != 0) {
388 PushErrorMessage(kErrorMessage
[kErrorEntryWithExpectationConflicts
],
392 entry
.test_expectation
=
393 (kTokenData
[token
].flag
| entry
.test_expectation
);
394 if (stage
== kLineParserEqual
)
402 if (stage
== kLineParserBegin
) {
403 // The whole line is empty or all comments
406 if (stage
== kLineParserExpectations
) {
407 if (!config
.IsValid()) {
408 PushErrorMessage(kErrorMessage
[kErrorInvalidEntry
], line_number
);
411 entries_
.push_back(entry
);
414 PushErrorMessage(kErrorMessage
[kErrorIllegalEntry
], line_number
);
418 bool GPUTestExpectationsParser::UpdateTestConfig(
419 GPUTestConfig
* config
, int32 token
, size_t line_number
) {
423 case kConfigWinVista
:
428 case kConfigMacLeopard
:
429 case kConfigMacSnowLeopard
:
431 case kConfigMacMountainLion
:
432 case kConfigMacMavericks
:
433 case kConfigMacYosemite
:
436 case kConfigChromeOS
:
438 if ((config
->os() & kTokenData
[token
].flag
) != 0) {
439 PushErrorMessage(kErrorMessage
[kErrorEntryWithOsConflicts
],
443 config
->set_os(config
->os() | kTokenData
[token
].flag
);
451 static_cast<uint32
>(kTokenData
[token
].flag
);
452 for (size_t i
= 0; i
< config
->gpu_vendor().size(); ++i
) {
453 if (config
->gpu_vendor()[i
] == gpu_vendor
) {
455 kErrorMessage
[kErrorEntryWithGpuVendorConflicts
],
460 config
->AddGPUVendor(gpu_vendor
);
465 if ((config
->build_type() & kTokenData
[token
].flag
) != 0) {
467 kErrorMessage
[kErrorEntryWithBuildTypeConflicts
],
471 config
->set_build_type(
472 config
->build_type() | kTokenData
[token
].flag
);
476 case kConfigGLDesktop
:
478 if ((config
->api() & kTokenData
[token
].flag
) != 0) {
479 PushErrorMessage(kErrorMessage
[kErrorEntryWithAPIConflicts
],
483 config
->set_api(config
->api() | kTokenData
[token
].flag
);
492 bool GPUTestExpectationsParser::UpdateTestConfig(
493 GPUTestConfig
* config
,
494 const std::string
& gpu_device_id
,
495 size_t line_number
) {
497 uint32 device_id
= 0;
498 if (config
->gpu_device_id() != 0 ||
499 !base::HexStringToUInt(gpu_device_id
, &device_id
) ||
501 PushErrorMessage(kErrorMessage
[kErrorEntryWithGpuDeviceIdConflicts
],
505 config
->set_gpu_device_id(device_id
);
509 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
511 for (size_t i
= 0; i
< entries_
.size(); ++i
) {
512 for (size_t j
= i
+ 1; j
< entries_
.size(); ++j
) {
513 if (entries_
[i
].test_name
== entries_
[j
].test_name
&&
514 entries_
[i
].test_config
.OverlapsWith(entries_
[j
].test_config
)) {
515 PushErrorMessage(kErrorMessage
[kErrorEntriesOverlap
],
516 entries_
[i
].line_number
,
517 entries_
[j
].line_number
);
525 void GPUTestExpectationsParser::PushErrorMessage(
526 const std::string
& message
, size_t line_number
) {
527 error_messages_
.push_back(
528 base::StringPrintf("Line %d : %s",
529 static_cast<int>(line_number
), message
.c_str()));
532 void GPUTestExpectationsParser::PushErrorMessage(
533 const std::string
& message
,
534 size_t entry1_line_number
,
535 size_t entry2_line_number
) {
536 error_messages_
.push_back(
537 base::StringPrintf("Line %d and %d : %s",
538 static_cast<int>(entry1_line_number
),
539 static_cast<int>(entry2_line_number
),
543 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
544 : test_expectation(0),