• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
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   kConfigMacMavericks,
40   kConfigMac,
41   kConfigLinux,
42   kConfigChromeOS,
43   kConfigAndroid,
44   // gpu vendor
45   kConfigNVidia,
46   kConfigAMD,
47   kConfigIntel,
48   kConfigVMWare,
49   // build type
50   kConfigRelease,
51   kConfigDebug,
52   // expectation
53   kExpectationPass,
54   kExpectationFail,
55   kExpectationFlaky,
56   kExpectationTimeout,
57   kExpectationSkip,
58   // separator
59   kSeparatorColon,
60   kSeparatorEqual,
61 
62   kNumberOfExactMatchTokens,
63 
64   // others
65   kConfigGPUDeviceID,
66   kTokenComment,
67   kTokenWord,
68 };
69 
70 struct TokenInfo {
71   const char* name;
72   int32 flag;
73 };
74 
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 },
90   { "nvidia", 0x10DE },
91   { "amd", 0x1002 },
92   { "intel", 0x8086 },
93   { "vmware", 0x15ad },
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 },
101   { ":", 0 },
102   { "=", 0 },
103 };
104 
105 enum ErrorType {
106   kErrorFileIO = 0,
107   kErrorIllegalEntry,
108   kErrorInvalidEntry,
109   kErrorEntryWithOsConflicts,
110   kErrorEntryWithGpuVendorConflicts,
111   kErrorEntryWithBuildTypeConflicts,
112   kErrorEntryWithGpuDeviceIdConflicts,
113   kErrorEntryWithExpectationConflicts,
114   kErrorEntriesOverlap,
115 
116   kNumberOfErrors,
117 };
118 
119 const char* kErrorMessage[] = {
120   "file IO failed",
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",
129 };
130 
ParseToken(const std::string & word)131 Token ParseToken(const std::string& word) {
132   if (StartsWithASCII(word, "//", false))
133     return kTokenComment;
134   if (StartsWithASCII(word, "0x", false))
135     return kConfigGPUDeviceID;
136 
137   for (int32 i = 0; i < kNumberOfExactMatchTokens; ++i) {
138     if (LowerCaseEqualsASCII(word, kTokenData[i].name))
139       return static_cast<Token>(i);
140   }
141   return kTokenWord;
142 }
143 
144 // reference name can have the last character as *.
NamesMatching(const std::string & ref,const std::string & test_name)145 bool NamesMatching(const std::string& ref, const std::string& test_name) {
146   size_t len = ref.length();
147   if (len == 0)
148     return false;
149   if (ref[len - 1] == '*') {
150     if (test_name.length() > len -1 &&
151         ref.compare(0, len - 1, test_name, 0, len - 1) == 0)
152       return true;
153     return false;
154   }
155   return (ref == test_name);
156 }
157 
158 }  // namespace anonymous
159 
GPUTestExpectationsParser()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]));
166 }
167 
~GPUTestExpectationsParser()168 GPUTestExpectationsParser::~GPUTestExpectationsParser() {
169 }
170 
LoadTestExpectations(const std::string & data)171 bool GPUTestExpectationsParser::LoadTestExpectations(const std::string& data) {
172   entries_.clear();
173   error_messages_.clear();
174 
175   std::vector<std::string> lines;
176   base::SplitString(data, '\n', &lines);
177   bool rt = true;
178   for (size_t i = 0; i < lines.size(); ++i) {
179     if (!ParseLine(lines[i], i + 1))
180       rt = false;
181   }
182   if (DetectConflictsBetweenEntries()) {
183     entries_.clear();
184     rt = false;
185   }
186 
187   return rt;
188 }
189 
LoadTestExpectations(const base::FilePath & path)190 bool GPUTestExpectationsParser::LoadTestExpectations(
191     const base::FilePath& path) {
192   entries_.clear();
193   error_messages_.clear();
194 
195   std::string data;
196   if (!base::ReadFileToString(path, &data)) {
197     error_messages_.push_back(kErrorMessage[kErrorFileIO]);
198     return false;
199   }
200   return LoadTestExpectations(data);
201 }
202 
GetTestExpectation(const std::string & test_name,const GPUTestBotConfig & bot_config) const203 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;
210   }
211   return kGpuTestPass;
212 }
213 
214 const std::vector<std::string>&
GetErrorMessages() const215 GPUTestExpectationsParser::GetErrorMessages() const {
216   return error_messages_;
217 }
218 
ParseConfig(const std::string & config_data,GPUTestConfig * config)219 bool GPUTestExpectationsParser::ParseConfig(
220     const std::string& config_data, GPUTestConfig* config) {
221   DCHECK(config);
222   std::vector<std::string> tokens;
223   base::SplitStringAlongWhitespace(config_data, &tokens);
224 
225   for (size_t i = 0; i < tokens.size(); ++i) {
226     Token token = ParseToken(tokens[i]);
227     switch (token) {
228       case kConfigWinXP:
229       case kConfigWinVista:
230       case kConfigWin7:
231       case kConfigWin8:
232       case kConfigWin:
233       case kConfigMacLeopard:
234       case kConfigMacSnowLeopard:
235       case kConfigMacLion:
236       case kConfigMacMountainLion:
237       case kConfigMacMavericks:
238       case kConfigMac:
239       case kConfigLinux:
240       case kConfigChromeOS:
241       case kConfigAndroid:
242       case kConfigNVidia:
243       case kConfigAMD:
244       case kConfigIntel:
245       case kConfigVMWare:
246       case kConfigRelease:
247       case kConfigDebug:
248       case kConfigGPUDeviceID:
249         if (token == kConfigGPUDeviceID) {
250           if (!UpdateTestConfig(config, tokens[i], 0))
251             return false;
252         } else {
253           if (!UpdateTestConfig(config, token, 0))
254             return false;
255         }
256         break;
257       default:
258         return false;
259     }
260   }
261   return true;
262 }
263 
ParseLine(const std::string & line_data,size_t line_number)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]);
275     switch (token) {
276       case kTokenComment:
277         comments_encountered = true;
278         break;
279       case kConfigWinXP:
280       case kConfigWinVista:
281       case kConfigWin7:
282       case kConfigWin8:
283       case kConfigWin:
284       case kConfigMacLeopard:
285       case kConfigMacSnowLeopard:
286       case kConfigMacLion:
287       case kConfigMacMountainLion:
288       case kConfigMacMavericks:
289       case kConfigMac:
290       case kConfigLinux:
291       case kConfigChromeOS:
292       case kConfigAndroid:
293       case kConfigNVidia:
294       case kConfigAMD:
295       case kConfigIntel:
296       case kConfigVMWare:
297       case kConfigRelease:
298       case kConfigDebug:
299       case kConfigGPUDeviceID:
300         // MODIFIERS, could be in any order, need at least one.
301         if (stage != kLineParserConfigs && stage != kLineParserBugID) {
302           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
303                            line_number);
304           return false;
305         }
306         if (token == kConfigGPUDeviceID) {
307           if (!UpdateTestConfig(&config, tokens[i], line_number))
308             return false;
309         } else {
310           if (!UpdateTestConfig(&config, token, line_number))
311             return false;
312         }
313         if (stage == kLineParserBugID)
314           stage++;
315         break;
316       case kSeparatorColon:
317         // :
318         if (stage != kLineParserConfigs) {
319           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
320                            line_number);
321           return false;
322         }
323         stage++;
324         break;
325       case kSeparatorEqual:
326         // =
327         if (stage != kLineParserTestName) {
328           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
329                            line_number);
330           return false;
331         }
332         stage++;
333         break;
334       case kTokenWord:
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];
340         } else {
341           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
342                            line_number);
343           return false;
344         }
345         stage++;
346         break;
347       case kExpectationPass:
348       case kExpectationFail:
349       case kExpectationFlaky:
350       case kExpectationTimeout:
351       case kExpectationSkip:
352         // TEST_EXPECTATIONS
353         if (stage != kLineParserEqual && stage != kLineParserExpectations) {
354           PushErrorMessage(kErrorMessage[kErrorIllegalEntry],
355                            line_number);
356           return false;
357         }
358         if ((kTokenData[token].flag & entry.test_expectation) != 0) {
359           PushErrorMessage(kErrorMessage[kErrorEntryWithExpectationConflicts],
360                            line_number);
361           return false;
362         }
363         entry.test_expectation =
364             (kTokenData[token].flag | entry.test_expectation);
365         if (stage == kLineParserEqual)
366           stage++;
367         break;
368       default:
369         DCHECK(false);
370         break;
371     }
372   }
373   if (stage == kLineParserBegin) {
374     // The whole line is empty or all comments
375     return true;
376   }
377   if (stage == kLineParserExpectations) {
378     if (!config.IsValid()) {
379         PushErrorMessage(kErrorMessage[kErrorInvalidEntry], line_number);
380         return false;
381     }
382     entries_.push_back(entry);
383     return true;
384   }
385   PushErrorMessage(kErrorMessage[kErrorIllegalEntry], line_number);
386   return false;
387 }
388 
UpdateTestConfig(GPUTestConfig * config,int32 token,size_t line_number)389 bool GPUTestExpectationsParser::UpdateTestConfig(
390     GPUTestConfig* config, int32 token, size_t line_number) {
391   DCHECK(config);
392   switch (token) {
393     case kConfigWinXP:
394     case kConfigWinVista:
395     case kConfigWin7:
396     case kConfigWin8:
397     case kConfigWin:
398     case kConfigMacLeopard:
399     case kConfigMacSnowLeopard:
400     case kConfigMacLion:
401     case kConfigMacMountainLion:
402     case kConfigMacMavericks:
403     case kConfigMac:
404     case kConfigLinux:
405     case kConfigChromeOS:
406     case kConfigAndroid:
407       if ((config->os() & kTokenData[token].flag) != 0) {
408         PushErrorMessage(kErrorMessage[kErrorEntryWithOsConflicts],
409                          line_number);
410         return false;
411       }
412       config->set_os(config->os() | kTokenData[token].flag);
413       break;
414     case kConfigNVidia:
415     case kConfigAMD:
416     case kConfigIntel:
417     case kConfigVMWare:
418       {
419         uint32 gpu_vendor =
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) {
423             PushErrorMessage(
424                 kErrorMessage[kErrorEntryWithGpuVendorConflicts],
425                 line_number);
426             return false;
427           }
428         }
429         config->AddGPUVendor(gpu_vendor);
430       }
431       break;
432     case kConfigRelease:
433     case kConfigDebug:
434       if ((config->build_type() & kTokenData[token].flag) != 0) {
435         PushErrorMessage(
436             kErrorMessage[kErrorEntryWithBuildTypeConflicts],
437             line_number);
438         return false;
439       }
440       config->set_build_type(
441           config->build_type() | kTokenData[token].flag);
442       break;
443     default:
444       DCHECK(false);
445       break;
446   }
447   return true;
448 }
449 
UpdateTestConfig(GPUTestConfig * config,const std::string & gpu_device_id,size_t line_number)450 bool GPUTestExpectationsParser::UpdateTestConfig(
451     GPUTestConfig* config,
452     const std::string& gpu_device_id,
453     size_t line_number) {
454   DCHECK(config);
455   uint32 device_id = 0;
456   if (config->gpu_device_id() != 0 ||
457       !base::HexStringToUInt(gpu_device_id, &device_id) ||
458       device_id == 0) {
459     PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
460                      line_number);
461     return false;
462   }
463   config->set_gpu_device_id(device_id);
464   return true;
465 }
466 
DetectConflictsBetweenEntries()467 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
468   bool rt = false;
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);
476         rt = true;
477       }
478     }
479   }
480   return rt;
481 }
482 
PushErrorMessage(const std::string & message,size_t 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()));
488 }
489 
PushErrorMessage(const std::string & message,size_t entry1_line_number,size_t entry2_line_number)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),
498                          message.c_str()));
499 }
500 
GPUTestExpectationEntry()501 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
502     : test_expectation(0),
503       line_number(0) {
504 }
505 
506 }  // namespace gpu
507 
508