diff --git a/arcanist/linter/AutoPEP8Linter.php b/arcanist/linter/AutoPEP8Linter.php --- a/arcanist/linter/AutoPEP8Linter.php +++ b/arcanist/linter/AutoPEP8Linter.php @@ -3,100 +3,112 @@ /** * Uses the autopep8 tool to format python code */ -final class AutoPEP8FormatLinter extends ArcanistExternalLinter { - - public function getInfoName() { - return 'autopep8'; - } - - public function getInfoURI() { - return ''; - } - - public function getInfoDescription() { - return pht('Use autopep8 for processing specified files.'); - } - - public function getLinterName() { - return 'autopep8'; - } - - public function getLinterConfigurationName() { - return 'autopep8'; - } - - public function getLinterConfigurationOptions() { - $options = array( - ); - - return $options + parent::getLinterConfigurationOptions(); - } - - public function getDefaultBinary() { - return 'autopep8'; - } - - public function getVersion() { - list($stdout, $stderr) = execx('%C --version', - $this->getExecutableCommand()); - $matches = array(); - - /* Support a.b or a.b.c version numbering scheme */ - $regex = '/^autopep8 (?P\d+\.\d+(?:\.\d+)?)/'; - - /* - * Old autopep8 output the version to stdout, newer output to stderr. - * Try both to determine the version. - */ - if (preg_match($regex, $stdout, $matches)) { - return $matches['version']; - } - if (preg_match($regex, $stderr, $matches)) { - return $matches['version']; +final class AutoPEP8FormatLinter extends ArcanistExternalLinter +{ + + public function getInfoName() + { + return 'autopep8'; + } + + public function getInfoURI() + { + return ''; + } + + public function getInfoDescription() + { + return pht('Use autopep8 for processing specified files.'); + } + + public function getLinterName() + { + return 'autopep8'; } - return false; - } - + public function getLinterConfigurationName() + { + return 'autopep8'; + } - public function getInstallInstructions() { - return pht('Make sure autopep8 is in directory specified by $PATH'); - } + public function getLinterConfigurationOptions() + { + $options = array(); - public function shouldExpectCommandErrors() { - return false; - } + return $options + parent::getLinterConfigurationOptions(); + } - protected function getMandatoryFlags() { - return array( - ); - } + public function getDefaultBinary() + { + return 'autopep8'; + } - protected function parseLinterOutput($path, $err, $stdout, $stderr) { - $ok = ($err == 0); + public function getVersion() + { + list($stdout, $stderr) = execx( + '%C --version', + $this->getExecutableCommand() + ); + $matches = array(); + + /* Support a.b or a.b.c version numbering scheme */ + $regex = '/^autopep8 (?P\d+\.\d+(?:\.\d+)?)/'; + + /* + * Old autopep8 output the version to stdout, newer output to stderr. + * Try both to determine the version. + */ + if (preg_match($regex, $stdout, $matches)) { + return $matches['version']; + } + if (preg_match($regex, $stderr, $matches)) { + return $matches['version']; + } + + return false; + } - if (!$ok) { - return false; + public function getInstallInstructions() + { + return pht('Make sure autopep8 is in directory specified by $PATH'); } - $root = $this->getProjectRoot(); - $path = Filesystem::resolvePath($path, $root); - $orig = file_get_contents($path); - if ($orig == $stdout) { - return array(); + public function shouldExpectCommandErrors() + { + return false; } - $message = id(new ArcanistLintMessage()) - ->setPath($path) - ->setLine(1) - ->setChar(1) - ->setGranularity(ArcanistLinter::GRANULARITY_FILE) - ->setCode('CFMT') - ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) - ->setName('Code style violation') - ->setDescription("'$path' has code style errors.") - ->setOriginalText($orig) - ->setReplacementText($stdout); - return array($message); - } + protected function getMandatoryFlags() + { + return array(); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) + { + $ok = ($err == 0); + + if (!$ok) { + return false; + } + + $root = $this->getProjectRoot(); + $path = Filesystem::resolvePath($path, $root); + $orig = file_get_contents($path); + if ($orig == $stdout) { + return array(); + } + + $message = id(new ArcanistLintMessage()) + ->setPath($path) + ->setLine(1) + ->setChar(1) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('CFMT') + ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) + ->setName('Code style violation') + ->setDescription("'$path' has code style errors.") + ->setOriginalText($orig) + ->setReplacementText($stdout); + return array($message); + } } diff --git a/arcanist/linter/CheckDocLinter.php b/arcanist/linter/CheckDocLinter.php --- a/arcanist/linter/CheckDocLinter.php +++ b/arcanist/linter/CheckDocLinter.php @@ -3,101 +3,113 @@ /** * Uses the check-doc.py script to enfore command line arguments documentation */ -final class CheckDocLinter extends ArcanistExternalLinter { - - public function getInfoName() { - return 'check-doc'; - } - - public function getInfoURI() { - return ''; - } - - public function getInfoDescription() { - return pht('Check that all the command line arguments are documented.'); - } - - public function getLinterName() { - return 'check-doc'; - } - - public function getLinterConfigurationName() { - return 'check-doc'; - } - - public function getLinterConfigurationOptions() { - $options = array( - ); - - return $options + parent::getLinterConfigurationOptions(); - } - - public function getDefaultBinary() { - return Filesystem::resolvePath( - 'test/lint/check-doc.py', - $this->getProjectRoot()); - } - - public function shouldUseInterpreter() { - return true; - } - - public function getDefaultInterpreter() { - return "python3"; - } - - public function getInstallInstructions() { - return pht('The test/lint/check-doc.py script is part of the bitcoin-abc project'); - } - - public function shouldExpectCommandErrors() { - return false; - } - - protected function getMandatoryFlags() { - return array( - ); - } - - protected function parseLinterOutput($path, $err, $stdout, $stderr) { - /* Split stdout: - * 0 => Empty (before first 'Args' occurence) - * 1 => Args used: count - * 2 => Args documented: count - * 3 => Args undocumented: count and list - * 4 => Args unknown: count and list - */ - $stdoutExploded = preg_split('/Args/', $stdout); - - $undocumented = $stdoutExploded[3]; - $unknown = $stdoutExploded[4]; - - $messages = array(); - - // Undocumented arguments - $match = preg_match_all('/-[\w|-]+/', $undocumented, $args); - foreach($args[0] as $arg) { - $messages[] = id(new ArcanistLintMessage()) - ->setGranularity(ArcanistLinter::GRANULARITY_GLOBAL) - ->setCode('ARGDOC') - ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) - ->setName('Undocumented argument') - ->setDescription("'$arg' is undocumented."); +final class CheckDocLinter extends ArcanistExternalLinter +{ + + public function getInfoName() + { + return 'check-doc'; } - - // Unknown arguments - $match = preg_match_all('/-[\w|-]+/', $unknown, $args); - foreach($args[0] as $arg) { - $messages[] = id(new ArcanistLintMessage()) - ->setGranularity(ArcanistLinter::GRANULARITY_GLOBAL) - ->setCode('ARGDOC') - ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) - ->setName('Unknown argument') - ->setDescription("'$arg' is documented but not used."); + + public function getInfoURI() + { + return ''; } - return $messages; - } -} + public function getInfoDescription() + { + return pht('Check that all the command line arguments are documented.'); + } + + public function getLinterName() + { + return 'check-doc'; + } + + public function getLinterConfigurationName() + { + return 'check-doc'; + } + + public function getLinterConfigurationOptions() + { + $options = array(); + + return $options + parent::getLinterConfigurationOptions(); + } + + public function getDefaultBinary() + { + return Filesystem::resolvePath( + 'test/lint/check-doc.py', + $this->getProjectRoot() + ); + } + + public function shouldUseInterpreter() + { + return true; + } -?> + public function getDefaultInterpreter() + { + return "python3"; + } + + public function getInstallInstructions() + { + return pht('The test/lint/check-doc.py script is part of the '. + 'bitcoin-abc project'); + } + + public function shouldExpectCommandErrors() + { + return false; + } + + protected function getMandatoryFlags() + { + return array(); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) + { + /* Split stdout: + * 0 => Empty (before first 'Args' occurence) + * 1 => Args used: count + * 2 => Args documented: count + * 3 => Args undocumented: count and list + * 4 => Args unknown: count and list + */ + $stdoutExploded = preg_split('/Args/', $stdout); + + $undocumented = $stdoutExploded[3]; + $unknown = $stdoutExploded[4]; + + $messages = array(); + + // Undocumented arguments + $match = preg_match_all('/-[\w|-]+/', $undocumented, $args); + foreach ($args[0] as $arg) { + $messages[] = id(new ArcanistLintMessage()) + ->setGranularity(ArcanistLinter::GRANULARITY_GLOBAL) + ->setCode('ARGDOC') + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Undocumented argument') + ->setDescription("'$arg' is undocumented."); + } + + // Unknown arguments + $match = preg_match_all('/-[\w|-]+/', $unknown, $args); + foreach ($args[0] as $arg) { + $messages[] = id(new ArcanistLintMessage()) + ->setGranularity(ArcanistLinter::GRANULARITY_GLOBAL) + ->setCode('ARGDOC') + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Unknown argument') + ->setDescription("'$arg' is documented but not used."); + } + + return $messages; + } +} diff --git a/arcanist/linter/ClangFormatLinter.php b/arcanist/linter/ClangFormatLinter.php --- a/arcanist/linter/ClangFormatLinter.php +++ b/arcanist/linter/ClangFormatLinter.php @@ -3,89 +3,100 @@ /** * Uses the clang format to format C/C++/Obj-C code */ -final class ClangFormatLinter extends ArcanistExternalLinter { +final class ClangFormatLinter extends ArcanistExternalLinter +{ - public function getInfoName() { - return 'clang-format'; - } - - public function getInfoURI() { - return ''; - } - - public function getInfoDescription() { - return pht('Use clang-format for processing specified files.'); - } - - public function getLinterName() { - return 'clang-format'; - } + public function getInfoName() + { + return 'clang-format'; + } - public function getLinterConfigurationName() { - return 'clang-format'; - } + public function getInfoURI() + { + return ''; + } - public function getLinterConfigurationOptions() { - $options = array( - ); + public function getInfoDescription() + { + return pht('Use clang-format for processing specified files.'); + } - return $options + parent::getLinterConfigurationOptions(); - } + public function getLinterName() + { + return 'clang-format'; + } - public function getDefaultBinary() { - return 'clang-format'; - } + public function getLinterConfigurationName() + { + return 'clang-format'; + } - public function getVersion() { - list($stdout) = execx('%C -version', $this->getExecutableCommand()); + public function getLinterConfigurationOptions() + { + $options = array(); - $matches = array(); - $regex = '/^clang-format version (?P\d+\.\d+)\./'; - if (preg_match($regex, $stdout, $matches)) { - return $matches['version']; - } else { - return false; + return $options + parent::getLinterConfigurationOptions(); } - } - - public function getInstallInstructions() { - return pht('Make sure clang-format is in directory specified by $PATH'); - } - public function shouldExpectCommandErrors() { - return false; - } + public function getDefaultBinary() + { + return 'clang-format'; + } - protected function getMandatoryFlags() { - return array( - ); - } + public function getVersion() + { + list($stdout) = execx('%C -version', $this->getExecutableCommand()); + + $matches = array(); + $regex = '/^clang-format version (?P\d+\.\d+)\./'; + if (preg_match($regex, $stdout, $matches)) { + return $matches['version']; + } else { + return false; + } + } - protected function parseLinterOutput($path, $err, $stdout, $stderr) { - $ok = ($err == 0); + public function getInstallInstructions() + { + return pht('Make sure clang-format is in directory specified by $PATH'); + } - if (!$ok) { - return false; + public function shouldExpectCommandErrors() + { + return false; } - $root = $this->getProjectRoot(); - $path = Filesystem::resolvePath($path, $root); - $orig = file_get_contents($path); - if ($orig == $stdout) { - return array(); + protected function getMandatoryFlags() + { + return array(); } - $message = id(new ArcanistLintMessage()) - ->setPath($path) - ->setLine(1) - ->setChar(1) - ->setGranularity(ArcanistLinter::GRANULARITY_FILE) - ->setCode('CFMT') - ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) - ->setName('Code style violation') - ->setDescription("'$path' has code style errors.") - ->setOriginalText($orig) - ->setReplacementText($stdout); - return array($message); - } + protected function parseLinterOutput($path, $err, $stdout, $stderr) + { + $ok = ($err == 0); + + if (!$ok) { + return false; + } + + $root = $this->getProjectRoot(); + $path = Filesystem::resolvePath($path, $root); + $orig = file_get_contents($path); + if ($orig == $stdout) { + return array(); + } + + $message = id(new ArcanistLintMessage()) + ->setPath($path) + ->setLine(1) + ->setChar(1) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('CFMT') + ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) + ->setName('Code style violation') + ->setDescription("'$path' has code style errors.") + ->setOriginalText($orig) + ->setReplacementText($stdout); + return array($message); + } } diff --git a/arcanist/linter/LocalDependenceLinter.php b/arcanist/linter/LocalDependenceLinter.php --- a/arcanist/linter/LocalDependenceLinter.php +++ b/arcanist/linter/LocalDependenceLinter.php @@ -4,129 +4,152 @@ * Uses the lint-locale-dependence.sh script to output a linting error when * functions relying on the system locale are used. */ -final class LocaleDependenceLinter extends ArcanistExternalLinter { - - const ADVICE_MESSAGE = <<getProjectRoot()); - } - - public function shouldUseInterpreter() { - return true; - } - - public function getDefaultInterpreter() { - return "bash"; - } - - public function getInstallInstructions() { - return pht('The test/lint/lint-locale-dependence.sh script is part of the - bitcoin-abc project. Requires git >= 2.6.5.'); - } - - public function shouldExpectCommandErrors() { - return false; - } - - protected function getMandatoryFlags() { - return array( - ); - } - - protected function parseLinterOutput($path, $err, $stdout, $stderr) { - /* - * Stdout contains 2 sections: - * 1/ The locale dependent function in a sentence: - * The locale dependent function (...) appears to be used - * Followed by the file and the corresponding code lines - * : - * 2/ Section with a general warning, will not be used by Arcanist. Instead - * we define our own advice message. - * - * First a list of the locale dependent functions will be determined, then - * the code lines containing these functions. - * An linting error message is generated for each line of code. - */ - - /* - * Extract infos from the path - */ - $pathinfo = pathinfo($path); - $fileName = $pathinfo['basename']; - - $messages = []; - - /* Find the functions */ - $pattern = '/The locale dependent function (\w+)\(...\)/'; - if (!preg_match_all($pattern, $stdout, $matches, $flags = PREG_SET_ORDER)) { - return $messages; + public function getInfoName() + { + return 'lint-locale-dependence'; } - $functions = []; - foreach($matches as $match) { - $functions[] = $match[1]; + public function getInfoURI() + { + return ''; } - /* Find the code lines */ - $pattern = '/'.$fileName.':(\d+): (.+)/'; - if (preg_match_all($pattern, $stdout, $matches, $flags = PREG_SET_ORDER)) { - foreach ($matches as $match) { - list(, $lineNumber, $codeSnippet) = $match; - - /* Determine which function is used */ - $functionUsed = ''; - foreach($functions as $function) { - if (strpos($codeSnippet, $function) !== FALSE) { - $functionUsed = $function; - break; - } - } + public function getInfoDescription() + { + return pht('Throw an error when functions relying on the system '. + 'locale are used.'); + } + + public function getLinterName() + { + return 'lint-locale-dependence'; + } + + public function getLinterConfigurationName() + { + return 'lint-locale-dependence'; + } + + public function getLinterConfigurationOptions() + { + $options = array(); + + return $options + parent::getLinterConfigurationOptions(); + } + + public function getDefaultBinary() + { + return Filesystem::resolvePath( + 'test/lint/lint-locale-dependence.sh', + $this->getProjectRoot() + ); + } + + public function shouldUseInterpreter() + { + return true; + } - $messages[] = id(new ArcanistLintMessage()) - ->setGranularity(ArcanistLinter::GRANULARITY_FILE) - ->setCode('LOCALE_DEPENDENCE') - ->setPath($path) - ->setLine($lineNumber) - ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) - ->setName('Locale dependent function '.$functionUsed) - ->setDescription(self::ADVICE_MESSAGE); - } + public function getDefaultInterpreter() + { + return "bash"; } - return $messages; - } + public function getInstallInstructions() + { + return pht('The test/lint/lint-locale-dependence.sh script is part of '. + 'the bitcoin-abc project. Requires git >= 2.6.5.'); + } + + public function shouldExpectCommandErrors() + { + return false; + } + + protected function getMandatoryFlags() + { + return array(); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) + { + /* + * Stdout contains 2 sections: + * 1/ The locale dependent function in a sentence: + * The locale dependent function (...) appears to be used + * Followed by the file and the corresponding code lines + * : + * 2/ Section with a general warning, will not be used by Arcanist. + * Instead we define our own advice message. + * + * First a list of the locale dependent functions will be determined, + * then the code lines containing these functions. + * An linting error message is generated for each line of code. + */ + + /* + * Extract infos from the path + */ + $pathinfo = pathinfo($path); + $fileName = $pathinfo['basename']; + + $messages = []; + + /* Find the functions */ + $pattern = '/The locale dependent function (\w+)\(...\)/'; + if (!preg_match_all( + $pattern, + $stdout, + $matches, + $flags = PREG_SET_ORDER + )) { + return $messages; + } + + $functions = []; + foreach ($matches as $match) { + $functions[] = $match[1]; + } + + /* Find the code lines */ + $pattern = '/'.$fileName.':(\d+): (.+)/'; + if (preg_match_all( + $pattern, + $stdout, + $matches, + $flags = PREG_SET_ORDER + )) { + foreach ($matches as $match) { + list(, $lineNumber, $codeSnippet) = $match; + + /* Determine which function is used */ + $functionUsed = ''; + foreach ($functions as $function) { + if (strpos($codeSnippet, $function) !== false) { + $functionUsed = $function; + break; + } + } + + $messages[] = id(new ArcanistLintMessage()) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('LOCALE_DEPENDENCE') + ->setPath($path) + ->setLine($lineNumber) + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Locale dependent function '.$functionUsed) + ->setDescription(self::ADVICE_MESSAGE); + } + } + + return $messages; + } } diff --git a/arcanist/linter/PythonFormatLinter.php b/arcanist/linter/PythonFormatLinter.php --- a/arcanist/linter/PythonFormatLinter.php +++ b/arcanist/linter/PythonFormatLinter.php @@ -1,95 +1,115 @@ getProjectRoot()); - } - - public function shouldUseInterpreter() { - return true; - } - - public function getDefaultInterpreter() { - return "python3"; - } - - public function getInstallInstructions() { - return pht('The test/lint/lint-python-format.py script is part of the bitcoin-abc project'); - } - - public function shouldExpectCommandErrors() { - return false; - } - - protected function getMandatoryFlags() { - return array(); - } - - protected function parseLinterOutput($path, $err, $stdout, $stderr) { - $pattern = '/\((\d+)\) ([\s\S]+?)=> (.+)/'; - $found = preg_match_all($pattern, $stdout, $snippets, - $flags = PREG_SET_ORDER); - - /* - * Matched snippets $snippets are organized like this: - * [0] The complete mask - * [1] The line number - * [2] The original snippet - * [3] The replacement snippet - */ - - if (!$found) { - return array(); +final class PythonFormatLinter extends ArcanistExternalLinter +{ + + public function getInfoName() + { + return 'lint-python-format'; + } + + public function getInfoURI() + { + return ''; + } + + public function getInfoDescription() + { + return pht('Convert python string formatting from % to .format().'); + } + + public function getLinterName() + { + return 'lint-python-format'; + } + + public function getLinterConfigurationName() + { + return 'lint-python-format'; + } + + public function getLinterConfigurationOptions() + { + $options = array(); + + return $options + parent::getLinterConfigurationOptions(); + } + + public function getDefaultBinary() + { + return Filesystem::resolvePath( + 'test/lint/lint-python-format.py', + $this->getProjectRoot() + ); } - $messages = []; - foreach($snippets as $snippet) { - $messages[] = id(new ArcanistLintMessage()) - ->setPath($path) - ->setLine($snippet[1]) - ->setChar(1) - ->setGranularity(ArcanistLinter::GRANULARITY_FILE) - ->setCode('PYFMT') - ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) - ->setName('Old string format notation') - ->setDescription("'$path' uses old style string formatting.") - ->setOriginalText(rtrim($snippet[2])) - ->setReplacementText($snippet[3]); + public function shouldUseInterpreter() + { + return true; + } + + public function getDefaultInterpreter() + { + return "python3"; + } + + public function getInstallInstructions() + { + return pht('The test/lint/lint-python-format.py script is part of the '. + 'bitcoin-abc project'); + } + + public function shouldExpectCommandErrors() + { + return false; + } + + protected function getMandatoryFlags() + { + return array(); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) + { + $pattern = '/\((\d+)\) ([\s\S]+?)=> (.+)/'; + $found = preg_match_all( + $pattern, + $stdout, + $snippets, + $flags = PREG_SET_ORDER + ); + + /* + * Matched snippets $snippets are organized like this: + * [0] The complete mask + * [1] The line number + * [2] The original snippet + * [3] The replacement snippet + */ + + if (!$found) { + return array(); + } + + $messages = []; + foreach ($snippets as $snippet) { + $messages[] = id(new ArcanistLintMessage()) + ->setPath($path) + ->setLine($snippet[1]) + ->setChar(1) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('PYFMT') + ->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX) + ->setName('Old string format notation') + ->setDescription("'$path' uses old style string formatting.") + ->setOriginalText(rtrim($snippet[2])) + ->setReplacementText($snippet[3]); + } + + return $messages; } - - return $messages; - } -} \ No newline at end of file +} diff --git a/arcanist/linter/TestsLinter.php b/arcanist/linter/TestsLinter.php --- a/arcanist/linter/TestsLinter.php +++ b/arcanist/linter/TestsLinter.php @@ -3,149 +3,164 @@ /** * Uses the lint-tests.sh script to enfore Boost unit tests name conformity */ -final class TestsLinter extends ArcanistExternalLinter { - - public function getInfoName() { - return 'lint-tests'; - } - - public function getInfoURI() { - return ''; - } - - public function getInfoDescription() { - return pht('Check the unit tests for duplicates and ensure the file name '. - 'matches the boost test suite name.'); - } - - public function getLinterName() { - return 'lint-tests'; - } - - public function getLinterConfigurationName() { - return 'lint-tests'; - } - - public function getLinterConfigurationOptions() { - $options = array( - ); - - return $options + parent::getLinterConfigurationOptions(); - } - - public function getDefaultBinary() { - return Filesystem::resolvePath( - 'test/lint/lint-tests.sh', - $this->getProjectRoot()); - } - - public function shouldUseInterpreter() { - return true; - } - - public function getDefaultInterpreter() { - return "bash"; - } - - public function getInstallInstructions() { - return pht('The test/lint/lint-tests.sh script is part of the bitcoin-abc '. - 'project'); - } - - public function shouldExpectCommandErrors() { - return false; - } - - protected function getMandatoryFlags() { - return array( - ); - } - - protected function parseLinterOutput($path, $err, $stdout, $stderr) { - /* - * Stdout contains 2 sections: - * 1/ Section with name mismatches, in the form: - * :BOOST_FIXTURE_TEST_SUITE(, ... - * 2/ Section with duplicated test names: - * - */ - - /* - * Extract infos from the path - * - * Note: the files are already filtered by path thanks to the .arclint - * configuration. If the file is not a test, the grep will find nothing and - * there will be no error to parse. - */ - $pathinfo = pathinfo($path); - $testName = $pathinfo['filename']; - $fileName = $pathinfo['basename']; - - $messages = []; - - /* Search for mismatch, using the line pattern */ - $pattern = '/'.$fileName.':BOOST_FIXTURE_TEST_SUITE\(([\w]+)/'; - $mismatch = preg_match($pattern, $stdout, $matches); - - if ($mismatch) { - /* - * Expect a single result as we are testing against a single file. - * - $matches[0] contains the full mask - * - $matches[1] contains the captured match - */ - if (count($matches) != 2) { - throw new Exception( - pht('Found multiple matches for a single file, lint-tests.sh output '. - 'is not formatted as expected, aborting.')); - } - - $mismatchName = $matches[1]; - $messages[] = id(new ArcanistLintMessage()) - ->setGranularity(ArcanistLinter::GRANULARITY_FILE) - ->setCode('TESTS') - ->setPath($path) - ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) - ->setName('Name mismatch') - ->setDescription( - 'The Boost test suite name must match the file name (set to "'. - $mismatchName.'", should be "'.$testName.'").'); +final class TestsLinter extends ArcanistExternalLinter +{ + + public function getInfoName() + { + return 'lint-tests'; } - /* - * Search for unicity, searching for test name alone on its line. - * The test name can be whether the one extracted from the file name or the - * one extracted from the BOOST_FIXTURE_TEST_SUITE content. - */ - if ($mismatch) { - $pattern = '/^'.$testName.'|'.$mismatchName.'$/'; - } else { - $pattern = '/^'.$testName.'$/'; + public function getInfoURI() + { + return ''; } - $notUnique = preg_match($pattern, $stdout, $matches); - - /* - * Do not check the number of matches here. - * Because it is a test against unicity, there is a global search which can - * possibly return an output matching our expected test name AND our actual - * test name. This would be weird, but not impossible. - * Just returning an error for the first one is enough, as the linter will - * be rerun after the name is fix and the other match will then eventually - * get catched. - */ - - if ($notUnique) { - $messages[] = id(new ArcanistLintMessage()) - ->setGranularity(ArcanistLinter::GRANULARITY_FILE) - ->setCode('TESTS') - ->setPath($path) - ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) - ->setName('Duplicated name') - ->setDescription('The test name "'.$matches[0].'" already exists'); + public function getInfoDescription() + { + return pht('Check the unit tests for duplicates and ensure the file '. + 'name matches the boost test suite name.'); } - return $messages; - } -} + public function getLinterName() + { + return 'lint-tests'; + } + + public function getLinterConfigurationName() + { + return 'lint-tests'; + } + + public function getLinterConfigurationOptions() + { + $options = array(); + + return $options + parent::getLinterConfigurationOptions(); + } + + public function getDefaultBinary() + { + return Filesystem::resolvePath( + 'test/lint/lint-tests.sh', + $this->getProjectRoot() + ); + } + + public function shouldUseInterpreter() + { + return true; + } + + public function getDefaultInterpreter() + { + return "bash"; + } -?> + public function getInstallInstructions() + { + return pht('The test/lint/lint-tests.sh script is part of the '. + 'bitcoin-abc project'); + } + + public function shouldExpectCommandErrors() + { + return false; + } + + protected function getMandatoryFlags() + { + return array(); + } + + protected function parseLinterOutput($path, $err, $stdout, $stderr) + { + /* + * Stdout contains 2 sections: + * 1/ Section with name mismatches, in the form: + * :BOOST_FIXTURE_TEST_SUITE(, ... + * 2/ Section with duplicated test names: + * + */ + + /* + * Extract infos from the path + * + * Note: the files are already filtered by path thanks to the .arclint + * configuration. If the file is not a test, the grep will find nothing + * and there will be no error to parse. + */ + $pathinfo = pathinfo($path); + $testName = $pathinfo['filename']; + $fileName = $pathinfo['basename']; + + $messages = []; + + /* Search for mismatch, using the line pattern */ + $pattern = '/'.$fileName.':BOOST_FIXTURE_TEST_SUITE\(([\w]+)/'; + $mismatch = preg_match($pattern, $stdout, $matches); + + if ($mismatch) { + /* + * Expect a single result as we are testing against a single file. + * - $matches[0] contains the full mask + * - $matches[1] contains the captured match + */ + if (count($matches) != 2) { + throw new Exception( + pht('Found multiple matches for a single file, '. + 'lint-tests.sh output is not formatted as expected, '. + 'aborting.') + ); + } + + $mismatchName = $matches[1]; + $messages[] = id(new ArcanistLintMessage()) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('TESTS') + ->setPath($path) + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Name mismatch') + ->setDescription( + 'The Boost test suite name must match the file name (set '. + 'to "'.$mismatchName.'", should be "'.$testName.'").' + ); + } + + /* + * Search for unicity, searching for test name alone on its line. + * The test name can be whether the one extracted from the file name or the + * one extracted from the BOOST_FIXTURE_TEST_SUITE content. + */ + if ($mismatch) { + $pattern = '/^'.$testName.'|'.$mismatchName.'$/'; + } else { + $pattern = '/^'.$testName.'$/'; + } + + $notUnique = preg_match($pattern, $stdout, $matches); + + /* + * Do not check the number of matches here. + * Because it is a test against unicity, there is a global search which + * can possibly return an output matching our expected test name AND our + * actual test name. This would be weird, but not impossible. + * Just returning an error for the first one is enough, as the linter + * will be rerun after the name is fix and the other match will then + * eventually get catched. + */ + + if ($notUnique) { + $messages[] = id(new ArcanistLintMessage()) + ->setGranularity(ArcanistLinter::GRANULARITY_FILE) + ->setCode('TESTS') + ->setPath($path) + ->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR) + ->setName('Duplicated name') + ->setDescription('The test name "'.$matches[0]. + '" already exists'); + } + + return $messages; + } +}