- add sources.
[platform/framework/web/crosswalk.git] / src / gpu / config / gpu_test_expectations_parser.cc
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.
4
5 #include "gpu/config/gpu_test_expectations_parser.h"
6
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"
13
14 namespace gpu {
15
16 namespace {
17
18 enum LineParserStage {
19   kLineParserBegin = 0,
20   kLineParserBugID,
21   kLineParserConfigs,
22   kLineParserColon,
23   kLineParserTestName,
24   kLineParserEqual,
25   kLineParserExpectations,
26 };
27
28 enum Token {
29   // os
30   kConfigWinXP = 0,
31   kConfigWinVista,
32   kConfigWin7,
33   kConfigWin8,
34   kConfigWin,
35   kConfigMacLeopard,
36   kConfigMacSnowLeopard,
37   kConfigMacLion,
38   kConfigMacMountainLion,
39   kConfigMac,
40   kConfigLinux,
41   kConfigChromeOS,
42   kConfigAndroid,
43   // gpu vendor
44   kConfigNVidia,
45   kConfigAMD,
46   kConfigIntel,
47   kConfigVMWare,
48   // build type
49   kConfigRelease,
50   kConfigDebug,
51   // expectation
52   kExpectationPass,
53   kExpectationFail,
54   kExpectationFlaky,
55   kExpectationTimeout,
56   kExpectationSkip,
57   // separator
58   kSeparatorColon,
59   kSeparatorEqual,
60
61   kNumberOfExactMatchTokens,
62
63   // others
64   kConfigGPUDeviceID,
65   kTokenComment,
66   kTokenWord,
67 };
68
69 struct TokenInfo {
70   const char* name;
71   int32 flag;
72 };
73
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 },
88   { "nvidia", 0x10DE },
89   { "amd", 0x1002 },
90   { "intel", 0x8086 },
91   { "vmware", 0x15ad },
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 },
99   { ":", 0 },
100   { "=", 0 },
101 };
102
103 enum ErrorType {
104   kErrorFileIO = 0,
105   kErrorIllegalEntry,
106   kErrorInvalidEntry,
107   kErrorEntryWithOsConflicts,
108   kErrorEntryWithGpuVendorConflicts,
109   kErrorEntryWithBuildTypeConflicts,
110   kErrorEntryWithGpuDeviceIdConflicts,
111   kErrorEntryWithExpectationConflicts,
112   kErrorEntriesOverlap,
113
114   kNumberOfErrors,
115 };
116
117 const char* kErrorMessage[] = {
118   "file IO failed",
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",
127 };
128
129 Token ParseToken(const std::string& word) {
130   if (StartsWithASCII(word, "//", false))
131     return kTokenComment;
132   if (StartsWithASCII(word, "0x", false))
133     return kConfigGPUDeviceID;
134
135   for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) {
136     if (LowerCaseEqualsASCII(word, kTokenData[i].name))
137       return static_cast<Token>(i);
138   }
139   return kTokenWord;
140 }
141
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();
145   if (len == 0)
146     return false;
147   if (ref[len - 1] == '*') {
148     if (test_name.length() > len -1 &&
149         ref.compare(0, len - 1, test_name, 0, len - 1) == 0)
150       return true;
151     return false;
152   }
153   return (ref == test_name);
154 }
155
156 }  // namespace anonymous
157
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]));
164 }
165
166 GPUTestExpectationsParser::~GPUTestExpectationsParser() {
167 }
168
169 bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) {
170   entries_.clear();
171   error_messages_.clear();
172
173   std::vector<std::string> lines;
174   base::SplitString(data, '\n', &lines);
175   bool rt = true;
176   for (size_t i = 0; i < lines.size(); ++i) {
177     if (!ParseLine(lines[i], i + 1))
178       rt = false;
179   }
180   if (DetectConflictsBetweenEntries()) {
181     entries_.clear();
182     rt = false;
183   }
184
185   return rt;
186 }
187
188 bool GPUTestExpectationsParser::LoadTestExpectations(
189     const base::FilePath& path) {
190   entries_.clear();
191   error_messages_.clear();
192
193   std::string data;
194   if (!base::ReadFileToString(path, &data)) {
195     error_messages_.push_back(kErrorMessage[kErrorFileIO]);
196     return false;
197   }
198   return LoadTestExpectations(data);
199 }
200
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;
208   }
209   return kGpuTestPass;
210 }
211
212 const std::vector<std::string>&
213 GPUTestExpectationsParser::GetErrorMessages() const {
214   return error_messages_;
215 }
216
217 bool GPUTestExpectationsParser::ParseConfig(
218     const std::string& config_data, GPUTestConfig* config) {
219   DCHECK(config);
220   std::vector<std::string> tokens;
221   base::SplitStringAlongWhitespace(config_data, &tokens);
222
223   for (size_t i = 0; i < tokens.size(); ++i) {
224     Token token = ParseToken(tokens[i]);
225     switch (token) {
226       case kConfigWinXP:
227       case kConfigWinVista:
228       case kConfigWin7:
229       case kConfigWin8:
230       case kConfigWin:
231       case kConfigMacLeopard:
232       case kConfigMacSnowLeopard:
233       case kConfigMacLion:
234       case kConfigMacMountainLion:
235       case kConfigMac:
236       case kConfigLinux:
237       case kConfigChromeOS:
238       case kConfigAndroid:
239       case kConfigNVidia:
240       case kConfigAMD:
241       case kConfigIntel:
242       case kConfigVMWare:
243       case kConfigRelease:
244       case kConfigDebug:
245       case kConfigGPUDeviceID:
246         if (token == kConfigGPUDeviceID) {
247           if (!UpdateTestConfig(config, tokens[i], 0))
248             return false;
249         } else {
250           if (!UpdateTestConfig(config, token, 0))
251             return false;
252         }
253         break;
254       default:
255         return false;
256     }
257   }
258   return true;
259 }
260
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]);
272     switch (token) {
273       case kTokenComment:
274         comments_encountered = true;
275         break;
276       case kConfigWinXP:
277       case kConfigWinVista:
278       case kConfigWin7:
279       case kConfigWin8:
280       case kConfigWin:
281       case kConfigMacLeopard:
282       case kConfigMacSnowLeopard:
283       case kConfigMacLion:
284       case kConfigMacMountainLion:
285       case kConfigMac:
286       case kConfigLinux:
287       case kConfigChromeOS:
288       case kConfigAndroid:
289       case kConfigNVidia:
290       case kConfigAMD:
291       case kConfigIntel:
292       case kConfigVMWare:
293       case kConfigRelease:
294       case kConfigDebug:
295       case kConfigGPUDeviceID:
296         // MODIFIERS, could be in any order, need at least one.
297         if (stage != kLineParserConfigs && stage != kLineParserBugID) {
298           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
299                            line_number);
300           return false;
301         }
302         if (token == kConfigGPUDeviceID) {
303           if (!UpdateTestConfig(&config, tokens[i], line_number))
304             return false;
305         } else {
306           if (!UpdateTestConfig(&config, token, line_number))
307             return false;
308         }
309         if (stage == kLineParserBugID)
310           stage++;
311         break;
312       case kSeparatorColon:
313         // :
314         if (stage != kLineParserConfigs) {
315           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
316                            line_number);
317           return false;
318         }
319         stage++;
320         break;
321       case kSeparatorEqual:
322         // =
323         if (stage != kLineParserTestName) {
324           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
325                            line_number);
326           return false;
327         }
328         stage++;
329         break;
330       case kTokenWord:
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];
336         } else {
337           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
338                            line_number);
339           return false;
340         }
341         stage++;
342         break;
343       case kExpectationPass:
344       case kExpectationFail:
345       case kExpectationFlaky:
346       case kExpectationTimeout:
347       case kExpectationSkip:
348         // TEST_EXPECTATIONS
349         if (stage != kLineParserEqual && stage != kLineParserExpectations) {
350           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
351                            line_number);
352           return false;
353         }
354         if ((kTokenData[token].flag & entry.test_expectation) != 0) {
355           PushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
356                            line_number);
357           return false;
358         }
359         entry.test_expectation =
360             (kTokenData[token].flag | entry.test_expectation);
361         if (stage == kLineParserEqual)
362           stage++;
363         break;
364       default:
365         DCHECK(false);
366         break;
367     }
368   }
369   if (stage == kLineParserBegin) {
370     // The whole line is empty or all comments
371     return true;
372   }
373   if (stage == kLineParserExpectations) {
374     if (!config.IsValid()) {
375         PushErrorMessage(kErrorMessage[kErrorInvalidEntry], line_number);
376         return false;
377     }
378     entries_.push_back(entry);
379     return true;
380   }
381   PushErrorMessage(kErrorMessage[kErrorIllegalEntry], line_number);
382   return false;
383 }
384
385 bool GPUTestExpectationsParser::UpdateTestConfig(
386     GPUTestConfig* config, int32 token, size_t line_number) {
387   DCHECK(config);
388   switch (token) {
389     case kConfigWinXP:
390     case kConfigWinVista:
391     case kConfigWin7:
392     case kConfigWin8:
393     case kConfigWin:
394     case kConfigMacLeopard:
395     case kConfigMacSnowLeopard:
396     case kConfigMacLion:
397     case kConfigMacMountainLion:
398     case kConfigMac:
399     case kConfigLinux:
400     case kConfigChromeOS:
401     case kConfigAndroid:
402       if ((config->os() & kTokenData[token].flag) != 0) {
403         PushErrorMessage(kErrorMessage[kErrorEntryWithOsConflicts],
404                          line_number);
405         return false;
406       }
407       config->set_os(config->os() | kTokenData[token].flag);
408       break;
409     case kConfigNVidia:
410     case kConfigAMD:
411     case kConfigIntel:
412     case kConfigVMWare:
413       {
414         uint32 gpu_vendor =
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) {
418             PushErrorMessage(
419                 kErrorMessage[kErrorEntryWithGpuVendorConflicts],
420                 line_number);
421             return false;
422           }
423         }
424         config->AddGPUVendor(gpu_vendor);
425       }
426       break;
427     case kConfigRelease:
428     case kConfigDebug:
429       if ((config->build_type() & kTokenData[token].flag) != 0) {
430         PushErrorMessage(
431             kErrorMessage[kErrorEntryWithBuildTypeConflicts],
432             line_number);
433         return false;
434       }
435       config->set_build_type(
436           config->build_type() | kTokenData[token].flag);
437       break;
438     default:
439       DCHECK(false);
440       break;
441   }
442   return true;
443 }
444
445 bool GPUTestExpectationsParser::UpdateTestConfig(
446     GPUTestConfig* config,
447     const std::string& gpu_device_id,
448     size_t line_number) {
449   DCHECK(config);
450   uint32 device_id = 0;
451   if (config->gpu_device_id() != 0 ||
452       !base::HexStringToUInt(gpu_device_id, &device_id) ||
453       device_id == 0) {
454     PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
455                      line_number);
456     return false;
457   }
458   config->set_gpu_device_id(device_id);
459   return true;
460 }
461
462 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
463   bool rt = false;
464   for (size_t i = 0; i < entries_.size(); ++i) {
465     for (size_t j = i + 1; j < entries_.size(); ++j) {
466       if (entries_[i].test_name == entries_[j].test_name &&
467           entries_[i].test_config.OverlapsWith(entries_[j].test_config)) {
468         PushErrorMessage(kErrorMessage[kErrorEntriesOverlap],
469                          entries_[i].line_number,
470                          entries_[j].line_number);
471         rt = true;
472       }
473     }
474   }
475   return rt;
476 }
477
478 void GPUTestExpectationsParser::PushErrorMessage(
479     const std::string& message, size_t line_number) {
480   error_messages_.push_back(
481       base::StringPrintf("Line %d : %s",
482                          static_cast<int>(line_number), message.c_str()));
483 }
484
485 void GPUTestExpectationsParser::PushErrorMessage(
486     const std::string& message,
487     size_t entry1_line_number,
488     size_t entry2_line_number) {
489   error_messages_.push_back(
490       base::StringPrintf("Line %d and %d : %s",
491                          static_cast<int>(entry1_line_number),
492                          static_cast<int>(entry2_line_number),
493                          message.c_str()));
494 }
495
496 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
497     : test_expectation(0),
498       line_number(0) {
499 }
500
501 }  // namespace gpu
502