Home | History | Annotate | Download | only in config
      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 (!file_util::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::HexStringToInt(gpu_device_id,
    453                             reinterpret_cast<int*>(&device_id)) ||
    454       device_id == 0) {
    455     PushErrorMessage(kErrorMessage[kErrorEntryWithGpuDeviceIdConflicts],
    456                      line_number);
    457     return false;
    458   }
    459   config->set_gpu_device_id(device_id);
    460   return true;
    461 }
    462 
    463 bool GPUTestExpectationsParser::DetectConflictsBetweenEntries() {
    464   bool rt = false;
    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);
    472         rt = true;
    473       }
    474     }
    475   }
    476   return rt;
    477 }
    478 
    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()));
    484 }
    485 
    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),
    494                          message.c_str()));
    495 }
    496 
    497 GPUTestExpectationsParser:: GPUTestExpectationEntry::GPUTestExpectationEntry()
    498     : test_expectation(0),
    499       line_number(0) {
    500 }
    501 
    502 }  // namespace gpu
    503 
    504