");
foreach ($violations as $violation) {
// This is going to be used as ID in HTML (deep anchoring).
@@ -401,7 +401,7 @@ class='info-lnk blck'
// Remove unnecessary tab/space characters at the line beginnings.
$html = self::reduceWhitespace($html);
- $w->write(sprintf($html, $excerptHtml));
+ $writer->write(sprintf($html, $excerptHtml));
}
}
@@ -458,8 +458,8 @@ protected static function colorize($message)
// Compile final regex, if not done already.
if (!self::$compiledHighlightRegex) {
$prepared = self::$descHighlightRules;
- array_walk($prepared, function (&$v, $k) {
- $v = "(?<{$k}>{$v['regex']})";
+ array_walk($prepared, function (&$value, $key) {
+ $value = "(?<{$key}>{$value['regex']})";
});
self::$compiledHighlightRegex = "#(" . implode('|', $prepared) . ")#";
@@ -467,13 +467,13 @@ protected static function colorize($message)
$rules = self::$descHighlightRules;
- return preg_replace_callback(self::$compiledHighlightRegex, function ($x) use ($rules) {
+ return preg_replace_callback(self::$compiledHighlightRegex, function ($matches) use ($rules) {
// Extract currently matched specification of highlighting (Match groups
// are named and we can find out which is not empty.).
- $definition = array_keys(array_intersect_key($rules, array_filter($x)));
+ $definition = array_keys(array_intersect_key($rules, array_filter($matches)));
$definition = reset($definition);
- return "
{$x[0]}";
+ return "
{$matches[0]}";
}, $message);
}
@@ -549,8 +549,8 @@ protected static function sumUpViolations($violations)
// We use "ref" reference to make things somewhat easier to read.
// Also, using a reference to non-existing array index doesn't throw a notice.
- if ($ns = $v->getNamespaceName()) {
- $ref = &$result[self::CATEGORY_NAMESPACE][$ns];
+ if ($namespaceName = $v->getNamespaceName()) {
+ $ref = &$result[self::CATEGORY_NAMESPACE][$namespaceName];
$ref = isset($ref) ? $ref + 1 : 1;
}
diff --git a/src/main/php/PHPMD/Renderer/JSONRenderer.php b/src/main/php/PHPMD/Renderer/JSONRenderer.php
index d91b9879c..a1c34ea77 100644
--- a/src/main/php/PHPMD/Renderer/JSONRenderer.php
+++ b/src/main/php/PHPMD/Renderer/JSONRenderer.php
@@ -45,7 +45,7 @@ public function renderReport(Report $report)
*
* @return array
*/
- private function initReportData()
+ protected function initReportData()
{
$data = array(
'version' => PHPMD::VERSION,
@@ -63,7 +63,7 @@ private function initReportData()
* @param array $data The report output to add the violations to.
* @return array The report output with violations, if any.
*/
- private function addViolationsToReport(Report $report, array $data)
+ protected function addViolationsToReport(Report $report, array $data)
{
$filesList = array();
/** @var RuleViolation $violation */
@@ -97,7 +97,7 @@ private function addViolationsToReport(Report $report, array $data)
* @param array $data The report output to add the errors to.
* @return array The report output with errors, if any.
*/
- private function addErrorsToReport(Report $report, array $data)
+ protected function addErrorsToReport(Report $report, array $data)
{
$errors = $report->getErrors();
if ($errors) {
diff --git a/src/main/php/PHPMD/Renderer/RendererFactory.php b/src/main/php/PHPMD/Renderer/RendererFactory.php
new file mode 100644
index 000000000..7796fc780
--- /dev/null
+++ b/src/main/php/PHPMD/Renderer/RendererFactory.php
@@ -0,0 +1,24 @@
+getStream());
+ $renderer = new BaselineRenderer(dirname($absolutePath));
+ $renderer->setWriter($writer);
+
+ return $renderer;
+ }
+}
diff --git a/src/main/php/PHPMD/Renderer/SARIFRenderer.php b/src/main/php/PHPMD/Renderer/SARIFRenderer.php
new file mode 100644
index 000000000..8962a976e
--- /dev/null
+++ b/src/main/php/PHPMD/Renderer/SARIFRenderer.php
@@ -0,0 +1,229 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Lukas Bestle
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\PHPMD;
+use PHPMD\Report;
+use PHPMD\Renderer\JSONRenderer;
+
+/**
+ * This class will render a SARIF (Static Analysis
+ * Results Interchange Format) report.
+ */
+class SARIFRenderer extends JSONRenderer
+{
+ /**
+ * Create report data and add renderer meta properties
+ *
+ * @return array
+ */
+ protected function initReportData()
+ {
+ $data = array(
+ 'version' => '2.1.0',
+ '$schema' =>
+ 'https://raw.githubusercontent.com/oasis-tcs/' .
+ 'sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
+ 'runs' => array(
+ array(
+ 'tool' => array(
+ 'driver' => array(
+ 'name' => 'PHPMD',
+ 'informationUri' => 'https://phpmd.org',
+ 'version' => PHPMD::VERSION,
+ 'rules' => array(),
+ ),
+ ),
+ 'originalUriBaseIds' => array(
+ 'WORKINGDIR' => array(
+ 'uri' => static::pathToUri(getcwd()) . '/',
+ ),
+ ),
+ 'results' => array(),
+ ),
+ ),
+ );
+
+ return $data;
+ }
+
+ /**
+ * Add violations, if any, to the report data
+ *
+ * @param Report $report The report with potential violations.
+ * @param array $data The report output to add the violations to.
+ * @return array The report output with violations, if any.
+ */
+ protected function addViolationsToReport(Report $report, array $data)
+ {
+ $rules = array();
+ $results = array();
+ $ruleIndices = array();
+
+ /** @var RuleViolation $violation */
+ foreach ($report->getRuleViolations() as $violation) {
+ $rule = $violation->getRule();
+ $ruleRef = str_replace(' ', '', $rule->getRuleSetName()) . '/' . $rule->getName();
+
+ if (!isset($ruleIndices[$ruleRef])) {
+ $ruleIndices[$ruleRef] = count($rules);
+
+ $ruleData = array(
+ 'id' => $ruleRef,
+ 'name' => $rule->getName(),
+ 'shortDescription' => array(
+ 'text' => $rule->getRuleSetName() . ': ' . $rule->getName(),
+ ),
+ 'messageStrings' => array(
+ 'default' => array(
+ 'text' => trim($rule->getMessage()),
+ ),
+ ),
+ 'help' => array(
+ 'text' => trim(str_replace("\n", ' ', $rule->getDescription())),
+ ),
+ 'helpUri' => $rule->getExternalInfoUrl(),
+ 'properties' => array(
+ 'ruleSet' => $rule->getRuleSetName(),
+ 'priority' => $rule->getPriority(),
+ ),
+ );
+
+ $examples = $rule->getExamples();
+ if (!empty($examples)) {
+ $ruleData['help']['markdown'] =
+ $ruleData['help']['text'] .
+ "\n\n### Example\n\n```php\n" .
+ implode("\n```\n\n```php\n", array_map('trim', $examples)) . "\n```";
+ }
+
+ $since = $rule->getSince();
+ if ($since) {
+ $ruleData['properties']['since'] = 'PHPMD ' . $since;
+ }
+
+ $rules[] = $ruleData;
+ }
+
+ $arguments = $violation->getArgs();
+ if ($arguments === null) {
+ $arguments = array();
+ }
+
+ $results[] = array(
+ 'ruleId' => $ruleRef,
+ 'ruleIndex' => $ruleIndices[$ruleRef],
+ 'message' => array(
+ 'id' => 'default',
+ 'arguments' => array_map('strval', $arguments),
+ 'text' => $violation->getDescription(),
+ ),
+ 'locations' => array(
+ array(
+ 'physicalLocation' => array(
+ 'artifactLocation' => static::pathToArtifactLocation($violation->getFileName()),
+ 'region' => array(
+ 'startLine' => $violation->getBeginLine(),
+ 'endLine' => $violation->getEndLine(),
+ ),
+ ),
+ )
+ ),
+ );
+ }
+
+ $data['runs'][0]['tool']['driver']['rules'] = $rules;
+ $data['runs'][0]['results'] = array_merge($data['runs'][0]['results'], $results);
+
+ return $data;
+ }
+
+ /**
+ * Add errors, if any, to the report data
+ *
+ * @param Report $report The report with potential errors.
+ * @param array $data The report output to add the errors to.
+ * @return array The report output with errors, if any.
+ */
+ protected function addErrorsToReport(Report $report, array $data)
+ {
+ $errors = $report->getErrors();
+ if ($errors) {
+ foreach ($errors as $error) {
+ $data['runs'][0]['results'][] = array(
+ 'level' => 'error',
+ 'message' => array(
+ 'text' => $error->getMessage(),
+ ),
+ 'locations' => array(
+ array(
+ 'physicalLocation' => array(
+ 'artifactLocation' => static::pathToArtifactLocation($error->getFile()),
+ ),
+ )
+ ),
+ );
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Makes an absolute path relative to the working directory
+ * if possible, otherwise prepends the `file://` protocol
+ * and returns the result as a SARIF `artifactLocation`
+ *
+ * @param string $path
+ * @return array
+ */
+ protected static function pathToArtifactLocation($path)
+ {
+ $workingDir = getcwd();
+ if (substr($path, 0, strlen($workingDir)) === $workingDir) {
+ // relative path
+ return array(
+ 'uri' => substr($path, strlen($workingDir) + 1),
+ 'uriBaseId' => 'WORKINGDIR',
+ );
+ }
+
+ // absolute path with protocol
+ return array(
+ 'uri' => static::pathToUri($path),
+ );
+ }
+
+ /**
+ * Converts an absolute path to a file:// URI
+ *
+ * @param string $path
+ * @return string
+ */
+ protected static function pathToUri($path)
+ {
+ $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
+
+ // file:///C:/... on Windows systems
+ if (substr($path, 0, 1) !== '/') {
+ $path = '/' . $path;
+ }
+
+ return 'file://' . $path;
+ }
+}
diff --git a/src/main/php/PHPMD/Renderer/XMLRenderer.php b/src/main/php/PHPMD/Renderer/XMLRenderer.php
index 218e04972..5c529f199 100644
--- a/src/main/php/PHPMD/Renderer/XMLRenderer.php
+++ b/src/main/php/PHPMD/Renderer/XMLRenderer.php
@@ -120,7 +120,7 @@ public function renderReport(Report $report)
* @param string $value The attribute value.
* @return void
*/
- private function maybeAdd($attr, $value)
+ protected function maybeAdd($attr, $value)
{
if ($value === null || trim($value) === '') {
return;
diff --git a/src/main/php/PHPMD/Report.php b/src/main/php/PHPMD/Report.php
index 5d4617e88..d7c0fd826 100644
--- a/src/main/php/PHPMD/Report.php
+++ b/src/main/php/PHPMD/Report.php
@@ -17,6 +17,8 @@
namespace PHPMD;
+use PHPMD\Baseline\BaselineValidator;
+
/**
* The report class collects all found violations and further information about
* a PHPMD run.
@@ -52,6 +54,14 @@ class Report
*/
private $errors = array();
+ /** @var BaselineValidator|null */
+ private $baselineValidator;
+
+ public function __construct(BaselineValidator $baselineValidator = null)
+ {
+ $this->baselineValidator = $baselineValidator;
+ }
+
/**
* Adds a rule violation to this report.
*
@@ -60,6 +70,10 @@ class Report
*/
public function addRuleViolation(RuleViolation $violation)
{
+ if ($this->baselineValidator !== null && $this->baselineValidator->isBaselined($violation)) {
+ return;
+ }
+
$fileName = $violation->getFileName();
if (!isset($this->ruleViolations[$fileName])) {
$this->ruleViolations[$fileName] = array();
diff --git a/src/main/php/PHPMD/Rule/AbstractLocalVariable.php b/src/main/php/PHPMD/Rule/AbstractLocalVariable.php
index a12a04a3c..84b71b303 100644
--- a/src/main/php/PHPMD/Rule/AbstractLocalVariable.php
+++ b/src/main/php/PHPMD/Rule/AbstractLocalVariable.php
@@ -277,8 +277,14 @@ protected function isPassedByReference($variable)
if ($parent && $parent instanceof ASTArguments) {
$argumentPosition = array_search($this->getNode($variable), $parent->getChildren());
$function = $this->getNode($parent->getParent());
+ $functionParent = $this->getNode($function->getParent());
$functionName = $function->getImage();
+ if ($functionParent instanceof ASTMemberPrimaryPrefix) {
+ // @TODO: Find a way to handle methods
+ return false;
+ }
+
try {
$reflectionFunction = new ReflectionFunction($functionName);
$parameters = $reflectionFunction->getParameters();
@@ -288,7 +294,6 @@ protected function isPassedByReference($variable)
}
} catch (ReflectionException $exception) {
// @TODO: Find a way to handle user-land functions
- // @TODO: Find a way to handle methods
}
}
diff --git a/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php b/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php
index 315f06bcd..423cf9b90 100644
--- a/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php
+++ b/src/main/php/PHPMD/Rule/CleanCode/MissingImport.php
@@ -43,6 +43,7 @@ class MissingImport extends AbstractRule implements MethodAware, FunctionAware
*/
public function apply(AbstractNode $node)
{
+ $ignoreGlobal = $this->getBooleanProperty('ignore-global');
foreach ($node->findChildrenOfType('AllocationExpression') as $allocationNode) {
if (!$allocationNode) {
continue;
@@ -54,9 +55,15 @@ public function apply(AbstractNode $node)
continue;
}
+ if ($ignoreGlobal && $this->isGlobalNamespace($classNode)) {
+ continue;
+ }
+
$classNameLength = $classNode->getEndColumn() - $classNode->getStartColumn() + 1;
- $fqcnLength = strlen($classNode->getImage());
- if ($classNameLength === $fqcnLength) {
+ $className = $classNode->getImage();
+ $fqcnLength = strlen($className);
+
+ if ($classNameLength === $fqcnLength && substr($className, 0, 1) !== '$') {
$this->addViolation($classNode, array($classNode->getBeginLine(), $classNode->getStartColumn()));
}
}
@@ -72,4 +79,15 @@ protected function isSelfReference(ASTNode $classNode)
{
return in_array($classNode->getImage(), $this->selfReferences, true);
}
+
+ /**
+ * Check whether a given class node is in the global namespace
+ *
+ * @param ASTNode $classNode A class node to check.
+ * @return bool Whether the given class node is in the global namespace.
+ */
+ protected function isGlobalNamespace(ASTNode $classNode)
+ {
+ return !strpos($classNode->getImage(), '\\', 1);
+ }
}
diff --git a/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php b/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php
index 9ea6cf67e..43849adf9 100644
--- a/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php
+++ b/src/main/php/PHPMD/Rule/Controversial/CamelCaseMethodName.php
@@ -74,11 +74,11 @@ public function apply(AbstractNode $node)
protected function isValid($methodName)
{
if ($this->getBooleanProperty('allow-underscore-test') && strpos($methodName, 'test') === 0) {
- return preg_match('/^test[a-zA-Z0-9]*([_][a-z][a-zA-Z0-9]*)?$/', $methodName);
+ return preg_match('/^test[a-zA-Z0-9]*(_[a-z][a-zA-Z0-9]*)*$/', $methodName);
}
if ($this->getBooleanProperty('allow-underscore')) {
- return preg_match('/^[_]?[a-z][a-zA-Z0-9]*$/', $methodName);
+ return preg_match('/^_?[a-z][a-zA-Z0-9]*$/', $methodName);
}
return preg_match('/^[a-z][a-zA-Z0-9]*$/', $methodName);
diff --git a/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php b/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php
index e35179989..f516e8554 100644
--- a/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php
+++ b/src/main/php/PHPMD/Rule/Design/TooManyPublicMethods.php
@@ -46,14 +46,19 @@ public function apply(AbstractNode $node)
$this->ignoreRegexp = $this->getStringProperty('ignorepattern');
$threshold = $this->getIntProperty('maxmethods');
- if ($node->getMetric('npm') <= $threshold) {
+ $publicMethodsCount = $node->getMetric('npm'); // NPM stands for Number of Public Methods
+
+ if ($publicMethodsCount !== null && $publicMethodsCount <= $threshold) {
return;
}
+
/** @var AbstractTypeNode $node */
$nom = $this->countMethods($node);
+
if ($nom <= $threshold) {
return;
}
+
$this->addViolation(
$node,
array(
@@ -75,11 +80,25 @@ protected function countMethods(AbstractTypeNode $node)
{
$count = 0;
foreach ($node->getMethods() as $method) {
- if ($method->getNode()->isPublic() && preg_match($this->ignoreRegexp, $method->getName()) === 0) {
+ if ($method->getNode()->isPublic() && !$this->isIgnoredMethodName($method->getName())) {
++$count;
}
}
return $count;
}
+
+ /**
+ * Return true if the method name is ignored by the current ignoreRegexp pattern.
+ *
+ * ignoreRegexp is given to the rule using ignorepattern option.
+ *
+ * @param string $methodName
+ * @return bool
+ */
+ private function isIgnoredMethodName($methodName)
+ {
+ return !empty($this->ignoreRegexp) &&
+ preg_match($this->ignoreRegexp, $methodName) === 1;
+ }
}
diff --git a/src/main/php/PHPMD/Rule/Naming/ShortVariable.php b/src/main/php/PHPMD/Rule/Naming/ShortVariable.php
index 4c391b51b..29f25e486 100644
--- a/src/main/php/PHPMD/Rule/Naming/ShortVariable.php
+++ b/src/main/php/PHPMD/Rule/Naming/ShortVariable.php
@@ -171,7 +171,9 @@ protected function getExceptionsList()
*/
protected function isNameAllowedInContext(AbstractNode $node)
{
- if ($this->isChildOf($node, 'ForeachStatement')) {
+ $parent = $node->getParent();
+
+ if ($parent && $parent->isInstanceOf('ForeachStatement')) {
return $this->isInitializedInLoop($node);
}
diff --git a/src/main/php/PHPMD/Rule/UnusedLocalVariable.php b/src/main/php/PHPMD/Rule/UnusedLocalVariable.php
index 491474dd0..8cef7a45d 100644
--- a/src/main/php/PHPMD/Rule/UnusedLocalVariable.php
+++ b/src/main/php/PHPMD/Rule/UnusedLocalVariable.php
@@ -200,7 +200,7 @@ protected function storeImage($imageName, ASTNode $node)
*/
protected function collectLiteral(ASTNode $node)
{
- $variable = '$' . trim($node->getImage(), '\'');
+ $variable = '$' . trim($node->getImage(), '\'\"');
if (!isset($this->images[$variable])) {
$this->images[$variable] = array();
diff --git a/src/main/php/PHPMD/RuleViolation.php b/src/main/php/PHPMD/RuleViolation.php
index 641b36dbb..8865f69e4 100644
--- a/src/main/php/PHPMD/RuleViolation.php
+++ b/src/main/php/PHPMD/RuleViolation.php
@@ -48,6 +48,14 @@ class RuleViolation
*/
private $description;
+ /**
+ * The arguments for the description/message text or null
+ * when the arguments are unknown.
+ *
+ * @var array|null
+ */
+ private $args = null;
+
/**
* The raw metric value which caused this rule violation.
*
@@ -66,7 +74,7 @@ class RuleViolation
* The name of a method or null when this violation has no method
* context.
*
- * @var string
+ * @var string|null
*/
private $methodName = null;
@@ -83,7 +91,7 @@ class RuleViolation
*
* @param \PHPMD\Rule $rule
* @param \PHPMD\AbstractNode $node
- * @param string $violationMessage
+ * @param string|array $violationMessage
* @param mixed $metric
*/
public function __construct(Rule $rule, AbstractNode $node, $violationMessage, $metric = null)
@@ -91,7 +99,20 @@ public function __construct(Rule $rule, AbstractNode $node, $violationMessage, $
$this->rule = $rule;
$this->node = $node;
$this->metric = $metric;
- $this->description = $violationMessage;
+
+ if (is_array($violationMessage) === true) {
+ $search = array();
+ $replace = array();
+ foreach ($violationMessage['args'] as $index => $value) {
+ $search[] = '{' . $index . '}';
+ $replace[] = $value;
+ }
+
+ $this->args = $violationMessage['args'];
+ $this->description = str_replace($search, $replace, $violationMessage['message']);
+ } else {
+ $this->description = $violationMessage;
+ }
if ($node instanceof AbstractTypeNode) {
$this->className = $node->getName();
@@ -123,6 +144,17 @@ public function getDescription()
return $this->description;
}
+ /**
+ * Returns the arguments for the description/message text or null
+ * when the arguments are unknown.
+ *
+ * @return array|null
+ */
+ public function getArgs()
+ {
+ return $this->args;
+ }
+
/**
* Returns the raw metric value which caused this rule violation.
*
@@ -188,7 +220,7 @@ public function getClassName()
* Returns the name of a method or null when this violation has no
* method context.
*
- * @return string
+ * @return string|null
*/
public function getMethodName()
{
diff --git a/src/main/php/PHPMD/TextUI/Command.php b/src/main/php/PHPMD/TextUI/Command.php
index 577da7d23..5bb10367b 100644
--- a/src/main/php/PHPMD/TextUI/Command.php
+++ b/src/main/php/PHPMD/TextUI/Command.php
@@ -9,16 +9,23 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD\TextUI;
+use PHPMD\Baseline\BaselineFileFinder;
+use PHPMD\Baseline\BaselineMode;
+use PHPMD\Baseline\BaselineSetFactory;
+use PHPMD\Baseline\BaselineValidator;
use PHPMD\PHPMD;
+use PHPMD\Renderer\RendererFactory;
+use PHPMD\Report;
use PHPMD\RuleSetFactory;
+use PHPMD\Utility\Paths;
use PHPMD\Writer\StreamWriter;
/**
@@ -31,7 +38,8 @@ class Command
*/
const EXIT_SUCCESS = 0,
EXIT_EXCEPTION = 1,
- EXIT_VIOLATION = 2;
+ EXIT_VIOLATION = 2,
+ EXIT_ERROR = 3;
/**
* This method creates a PHPMD instance and configures this object based
@@ -40,13 +48,14 @@ class Command
* The return value of this method can be used as an exit code. A value
* equal to EXIT_SUCCESS means that no violations or errors were
* found in the analyzed code. Otherwise this method will return a value
- * equal to EXIT_VIOLATION.
+ * equal to EXIT_VIOLATION or EXIT_ERROR respectively.
*
- * The use of flag --ignore-violations-on-exit will result to a
- * EXIT_SUCCESS even if any violation is found.
+ * The use of the flags --ignore-violations-on-exit and
+ * --ignore-errors-on-exit will result to a EXIT_SUCCESS
+ * even if any violation or error is found.
*
* @param \PHPMD\TextUI\CommandLineOptions $opts
- * @param \PHPMD\RuleSetFactory $ruleSetFactory
+ * @param \PHPMD\RuleSetFactory $ruleSetFactory
* @return integer
*/
public function run(CommandLineOptions $opts, RuleSetFactory $ruleSetFactory)
@@ -73,6 +82,26 @@ public function run(CommandLineOptions $opts, RuleSetFactory $ruleSetFactory)
$renderers[] = $reportRenderer;
}
+ // Configure baseline violations
+ $report = null;
+ $finder = new BaselineFileFinder($opts);
+ if ($opts->generateBaseline() === BaselineMode::GENERATE) {
+ // overwrite any renderer with the baseline renderer
+ $renderers = array(RendererFactory::createBaselineRenderer(new StreamWriter($finder->notNull()->find())));
+ } elseif ($opts->generateBaseline() === BaselineMode::UPDATE) {
+ $baselineFile = $finder->notNull()->existingFile()->find();
+ $baseline = BaselineSetFactory::fromFile(Paths::getRealPath($baselineFile));
+ $renderers = array(RendererFactory::createBaselineRenderer(new StreamWriter($baselineFile)));
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::UPDATE));
+ } else {
+ // try to locate a baseline file and read it
+ $baselineFile = $finder->existingFile()->find();
+ if ($baselineFile !== null) {
+ $baseline = BaselineSetFactory::fromFile(Paths::getRealPath($baselineFile));
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::NONE));
+ }
+ }
+
// Configure a rule set factory
$ruleSetFactory->setMinimumPriority($opts->getMinimumPriority());
$ruleSetFactory->setMaximumPriority($opts->getMaximumPriority());
@@ -96,17 +125,24 @@ public function run(CommandLineOptions $opts, RuleSetFactory $ruleSetFactory)
$ignore = $opts->getIgnore();
if ($ignore !== null) {
- $phpmd->setIgnorePattern(explode(',', $ignore));
+ $phpmd->addIgnorePatterns(explode(',', $ignore));
}
$phpmd->processFiles(
$opts->getInputPath(),
$opts->getRuleSets(),
$renderers,
- $ruleSetFactory
+ $ruleSetFactory,
+ $report !== null ? $report : new Report()
);
- if ($phpmd->hasViolations() && !$opts->ignoreViolationsOnExit()) {
+ if ($phpmd->hasErrors() && !$opts->ignoreErrorsOnExit()) {
+ return self::EXIT_ERROR;
+ }
+
+ if ($phpmd->hasViolations()
+ && !$opts->ignoreViolationsOnExit()
+ && $opts->generateBaseline() === BaselineMode::NONE) {
return self::EXIT_VIOLATION;
}
@@ -124,7 +160,7 @@ private function getVersion()
$version = '@package_version@';
if (file_exists($build)) {
- $data = @parse_ini_file($build);
+ $data = @parse_ini_file($build);
$version = $data['project.version'];
}
@@ -142,8 +178,8 @@ public static function main(array $args)
{
try {
$ruleSetFactory = new RuleSetFactory();
- $options = new CommandLineOptions($args, $ruleSetFactory->listAvailableRuleSets());
- $command = new Command();
+ $options = new CommandLineOptions($args, $ruleSetFactory->listAvailableRuleSets());
+ $command = new Command();
$exitCode = $command->run($options, $ruleSetFactory);
} catch (\Exception $e) {
diff --git a/src/main/php/PHPMD/TextUI/CommandLineOptions.php b/src/main/php/PHPMD/TextUI/CommandLineOptions.php
index b0b0fe515..dc7a2acea 100644
--- a/src/main/php/PHPMD/TextUI/CommandLineOptions.php
+++ b/src/main/php/PHPMD/TextUI/CommandLineOptions.php
@@ -9,18 +9,22 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD\TextUI;
+use PHPMD\Baseline\BaselineMode;
use PHPMD\Renderer\AnsiRenderer;
+use PHPMD\Renderer\GitHubRenderer;
use PHPMD\Renderer\HTMLRenderer;
use PHPMD\Renderer\JSONRenderer;
+use PHPMD\Renderer\SARIFRenderer;
use PHPMD\Renderer\TextRenderer;
+use PHPMD\Renderer\CheckStyleRenderer;
use PHPMD\Renderer\XMLRenderer;
use PHPMD\Rule;
@@ -124,6 +128,14 @@ class CommandLineOptions
*/
protected $strict = false;
+ /**
+ * Should PHPMD exit without error code even if error is found?
+ *
+ * @var boolean
+ * @since 2.10.0
+ */
+ protected $ignoreErrorsOnExit = false;
+
/**
* Should PHPMD exit without error code even if violation is found?
*
@@ -138,6 +150,19 @@ class CommandLineOptions
*/
protected $availableRuleSets = array();
+ /**
+ * Should PHPMD baseline the existing violations and write them to the $baselineFile
+ * @var string allowed modes: NONE, GENERATE or UPDATE
+ */
+ protected $generateBaseline = BaselineMode::NONE;
+
+ /**
+ * The baseline source file to read the baseline violations from.
+ * Defaults to the path of the (first) ruleset file as phpmd.baseline.xml
+ * @var string|null
+ */
+ protected $baselineFile;
+
/**
* Constructs a new command line options instance.
*
@@ -202,14 +227,28 @@ public function __construct(array $args, array $availableRuleSets = array())
case '--not-strict':
$this->strict = false;
break;
+ case '--generate-baseline':
+ $this->generateBaseline = BaselineMode::GENERATE;
+ break;
+ case '--update-baseline':
+ $this->generateBaseline = BaselineMode::UPDATE;
+ break;
+ case '--baseline-file':
+ $this->baselineFile = array_shift($args);
+ break;
+ case '--ignore-errors-on-exit':
+ $this->ignoreErrorsOnExit = true;
+ break;
case '--ignore-violations-on-exit':
$this->ignoreViolationsOnExit = true;
break;
+ case '--reportfile-checkstyle':
case '--reportfile-html':
+ case '--reportfile-json':
+ case '--reportfile-sarif':
case '--reportfile-text':
case '--reportfile-xml':
- case '--reportfile-json':
- preg_match('(^\-\-reportfile\-(xml|html|text|json)$)', $arg, $match);
+ preg_match('(^\-\-reportfile\-(checkstyle|html|json|sarif|text|xml)$)', $arg, $match);
$this->reportFiles[$match[1]] = array_shift($args);
break;
default:
@@ -222,9 +261,9 @@ public function __construct(array $args, array $availableRuleSets = array())
throw new \InvalidArgumentException($this->usage(), self::INPUT_ERROR);
}
- $this->inputPath = (string)array_shift($arguments);
+ $this->inputPath = (string)array_shift($arguments);
$this->reportFormat = (string)array_shift($arguments);
- $this->ruleSets = (string)array_shift($arguments);
+ $this->ruleSets = (string)array_shift($arguments);
}
/**
@@ -353,6 +392,37 @@ public function hasStrict()
return $this->strict;
}
+ /**
+ * Should the current violations be baselined
+ *
+ * @return string
+ */
+ public function generateBaseline()
+ {
+ return $this->generateBaseline;
+ }
+
+ /**
+ * The filepath of the baseline violations xml
+ *
+ * @return string|null
+ */
+ public function baselineFile()
+ {
+ return $this->baselineFile;
+ }
+
+ /**
+ * Was the --ignore-errors-on-exit passed to PHPMD's command line interface?
+ *
+ * @return boolean
+ * @since 2.10.0
+ */
+ public function ignoreErrorsOnExit()
+ {
+ return $this->ignoreErrorsOnExit;
+ }
+
/**
* Was the --ignore-violations-on-exit passed to PHPMD's command line interface?
*
@@ -384,16 +454,22 @@ public function createRenderer($reportFormat = null)
$reportFormat = $reportFormat ?: $this->reportFormat;
switch ($reportFormat) {
- case 'xml':
- return $this->createXmlRenderer();
+ case 'ansi':
+ return $this->createAnsiRenderer();
+ case 'checkstyle':
+ return $this->createCheckStyleRenderer();
+ case 'github':
+ return $this->createGitHubRenderer();
case 'html':
return $this->createHtmlRenderer();
- case 'text':
- return $this->createTextRenderer();
case 'json':
return $this->createJsonRenderer();
- case 'ansi':
- return $this->createAnsiRenderer();
+ case 'sarif':
+ return $this->createSarifRenderer();
+ case 'text':
+ return $this->createTextRenderer();
+ case 'xml':
+ return $this->createXmlRenderer();
default:
return $this->createCustomRenderer();
}
@@ -423,6 +499,14 @@ protected function createAnsiRenderer()
return new AnsiRenderer();
}
+ /**
+ * @return \PHPMD\Renderer\GitHubRenderer
+ */
+ protected function createGitHubRenderer()
+ {
+ return new GitHubRenderer();
+ }
+
/**
* @return \PHPMD\Renderer\HTMLRenderer
*/
@@ -439,6 +523,22 @@ protected function createJsonRenderer()
return new JSONRenderer();
}
+ /**
+ * @return \PHPMD\Renderer\JSONRenderer
+ */
+ protected function createCheckStyleRenderer()
+ {
+ return new CheckStyleRenderer();
+ }
+
+ /**
+ * @return \PHPMD\Renderer\SARIFRenderer
+ */
+ protected function createSarifRenderer()
+ {
+ return new SARIFRenderer();
+ }
+
/**
* @return \PHPMD\AbstractRenderer
* @throws \InvalidArgumentException
@@ -507,8 +607,14 @@ public function usage()
'For example *src/foo/*.php or *src/foo/*' . \PHP_EOL .
'--strict: also report those nodes with a @SuppressWarnings ' .
'annotation' . \PHP_EOL .
+ '--ignore-errors-on-exit: will exit with a zero code, ' .
+ 'even on error' . \PHP_EOL .
'--ignore-violations-on-exit: will exit with a zero code, ' .
- 'even if any violations are found' . \PHP_EOL;
+ 'even if any violations are found' . \PHP_EOL .
+ '--generate-baseline: will generate a phpmd.baseline.xml next ' .
+ 'to the first ruleset file location' . \PHP_EOL .
+ '--update-baseline: will remove any non-existing violations from the phpmd.baseline.xml' . \PHP_EOL .
+ '--baseline-file: a custom location of the baseline file' . \PHP_EOL;
}
/**
@@ -519,7 +625,7 @@ public function usage()
protected function getListOfAvailableRenderers()
{
$renderersDirPathName = __DIR__ . '/../Renderer';
- $renderers = array();
+ $renderers = array();
foreach (scandir($renderersDirPathName) as $rendererFileName) {
if (preg_match('/^(\w+)Renderer.php$/i', $rendererFileName, $rendererName)) {
diff --git a/src/main/php/PHPMD/Utility/Paths.php b/src/main/php/PHPMD/Utility/Paths.php
new file mode 100644
index 000000000..fd7555d53
--- /dev/null
+++ b/src/main/php/PHPMD/Utility/Paths.php
@@ -0,0 +1,71 @@
+stream, $data);
}
+
+ /**
+ * @return resource
+ */
+ public function getStream()
+ {
+ return $this->stream;
+ }
}
diff --git a/src/main/resources/rulesets/cleancode.xml b/src/main/resources/rulesets/cleancode.xml
index bfb58c5b5..ca1ebaab6 100644
--- a/src/main/resources/rulesets/cleancode.xml
+++ b/src/main/resources/rulesets/cleancode.xml
@@ -191,6 +191,9 @@ Importing all external classes in a file through use statements makes them clear
]]>
1
+
+
+
Suppress warnings
suppress-warnings.rst
+
+ CI Integration
+ ci-integration.rst
+
Writing a PHPMD Rule
writing-a-phpmd-rule.rst
diff --git a/src/site/rst/documentation/ci-integration.rst b/src/site/rst/documentation/ci-integration.rst
new file mode 100644
index 000000000..270154572
--- /dev/null
+++ b/src/site/rst/documentation/ci-integration.rst
@@ -0,0 +1,35 @@
+==============
+CI Integration
+==============
+
+PHPMD can be integrated into continuous integration (CI) pipelines to verify that each code change conforms to the configured rules.
+
+GitHub Actions
+==============
+
+GitHub Actions is supported out of the box with its own PHPMD renderer called ``github``. This renderer will add annotations directly to your commits and pull requests right in the code.
+
+A simple GitHub Actions workflow could look like this: ::
+
+ name: CI
+
+ on: push
+
+ jobs:
+ phpmd:
+ name: PHPMD
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Setup PHP environment
+ uses: shivammathur/setup-php@v2
+ with:
+ coverage: none
+ tools: phpmd
+
+ - name: Run PHPMD
+ run: phpmd . github phpmd.ruleset.xml --exclude 'tests/*,vendor/*'
+
+This assumes that you have a `custom rule set `_ in the file ``phpmd.ruleset.xml``. Alternatively, you can of course list the rule sets manually.
diff --git a/src/site/rst/documentation/index.rst b/src/site/rst/documentation/index.rst
index 3a4a6ddae..7966a98e9 100644
--- a/src/site/rst/documentation/index.rst
+++ b/src/site/rst/documentation/index.rst
@@ -59,9 +59,20 @@ Command line options
- ``--not-strict`` - Does not report those nodes with a @SuppressWarnings annotation.
+ - ``--ignore-errors-on-exit`` - will exit with a zero code, even on error.
+
- ``--ignore-violations-on-exit`` - will exit with a zero code, even if any
violations are found.
+ - ``--generate-baseline`` - will generate a ``phpmd.baseline.xml`` for existing violations
+ next to the ruleset definition file.
+
+ - ``--update-baseline`` - will remove all violations from an existing ``phpmd.baseline.xml``
+ that no longer exist. New violations will _not_ be added.
+
+ - ``--baseline-file`` - the filepath to a custom baseline xml file. The filepath
+ of all baselined files must be relative to this file location.
+
An example command line: ::
phpmd PHP/Depend/DbusUI xml codesize --reportfile phpmd.xml --suffixes php,phtml
@@ -92,18 +103,21 @@ that will check the source code.
Exit codes
==========
-PHPMD's command line tool currently defines three different exit codes.
+PHPMD's command line tool currently defines four different exit codes.
- *0*, This exit code indicates that everything worked as expected. This means
there was no error/exception and PHPMD hasn't detected any rule violation
in the code under test.
-- *1*, This exit code indicates that an error/exception occurred which has
+- *1*, This exit code indicates that an exception occurred which has
interrupted PHPMD during execution.
- *2*, This exit code means that PHPMD has processed the code under test
without the occurrence of an error/exception, but it has detected rule
violations in the analyzed source code. You can also prevent this behaviour
with the ``--ignore-violations-on-exit`` flag, which will result to a *0*
even if any violations are found.
+- *3*, This exit code means that one or multiple files under test could not
+ be processed because of an error. There may also be violations in other
+ files that could be processed correctly.
Renderers
=========
@@ -112,6 +126,28 @@ At the moment PHPMD comes with the following five renderers:
- *xml*, which formats the report as XML.
- *text*, simple textual format.
-- *ansi*, colorful, formated text for the command line.
+- *ansi*, colorful, formatted text for the command line.
- *html*, single HTML file with possible problems.
- *json*, formats JSON report.
+- *github*, a format that GitHub Actions understands (see `CI Integration `_).
+
+Baseline
+=========
+
+For existing projects a violation baseline can be generated. All violations in this baseline will be ignored in further inspections.
+
+The recommended approach would be a ``phpmd.xml`` in the root of the project. To generate the phpmd.baseline.xml next to it::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --generate-baseline
+
+To specify a custom baseline filepath for export::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --generate-baseline --baseline-file /path/to/source/phpmd.baseline.xml
+
+By default PHPMD will look next to ``phpmd.xml`` for ``phpmd.baseline.xml``. To overwrite this behaviour::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --baseline-file /path/to/source/phpmd.baseline.xml
+
+To clean up an existing baseline file and *only remove* no longer existing violations::
+
+ ~ $ phpmd /path/to/source text phpmd.xml --update-baseline
diff --git a/src/site/rst/robots.txt b/src/site/rst/robots.txt
deleted file mode 100644
index 17a3cf411..000000000
--- a/src/site/rst/robots.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# robots.txt for http://kore-nordmann.de
-#
-# Please note: There are a lot of pages on this site, and there are
-# some misbehaved spiders out there that go _way_ too fast. If you're
-# irresponsible, your access to the site may be blocked.
-
-Sitemap: http://pdepend.org/.sitemap
-
-# advertising-related bots:
-User-agent: Mediapartners-Google*
-Disallow: /
-
-# A capture bot, downloads gazillions of pages with no public benefit
-# http://www.webreaper.net/
-User-agent: WebReaper
-Disallow: /
-
-#
-# Friendly, low-speed bots are welcome viewing article pages, but not
-# dynamically-generated pages please.
-#
-
-# Only googles bots now '$'
-# User-agent: Googlebot
-# Disallow: /relation/find$
-
-User-agent: *
-Disallow: /styles/
-Disallow: /images/
-
-#
-# *at least* 1 second please. preferably more :D
-#
-Crawl-delay: 1
diff --git a/src/site/rst/rules/cleancode.rst b/src/site/rst/rules/cleancode.rst
index 84f9233c4..498f92ca6 100644
--- a/src/site/rst/rules/cleancode.rst
+++ b/src/site/rst/rules/cleancode.rst
@@ -132,6 +132,14 @@ Example: ::
return new \stdClass();
}
+This rule has the following properties:
+
++-----------------------------------+---------------+------------------------------------------------------------+
+| Name | Default Value | Description |
++===================================+===============+============================================================+
+| ignore-global | | Ignore classes, interfaces and traits in the global namespace |
++-----------------------------------+---------------+------------------------------------------------------------+
+
UndefinedVariable
=================
@@ -152,4 +160,3 @@ Remark
This document is based on a ruleset xml-file, that was taken from the original source of the `PMD`__ project. This means that most parts of the content on this page are the intellectual work of the PMD community and its contributors and not of the PHPMD project.
__ http://pmd.sourceforge.net/
-
diff --git a/src/test/php/PHPMD/AbstractStaticTest.php b/src/test/php/PHPMD/AbstractStaticTest.php
index 1037d736c..31991f549 100644
--- a/src/test/php/PHPMD/AbstractStaticTest.php
+++ b/src/test/php/PHPMD/AbstractStaticTest.php
@@ -17,6 +17,7 @@
namespace PHPMD;
+use Closure;
use PHPUnit_Framework_TestCase;
/**
@@ -66,6 +67,8 @@ protected static function returnToOriginalWorkingDirectory()
*/
protected static function cleanupTempFiles()
{
+ // cleanup any open resources on temp files
+ gc_collect_cycles();
foreach (self::$tempFiles as $tempFile) {
unlink($tempFile);
}
@@ -150,21 +153,27 @@ public static function assertXmlEquals($actualOutput, $expectedFileName)
*
* @param string $actualOutput Generated JSON output.
* @param string $expectedFileName File with expected JSON result.
+ * @param bool|Closure $removeDynamicValues If set to `false`, the actual output is not normalized,
+ * if set to a closure, the closure is applied on the actual output array.
*
* @return void
*/
- public static function assertJsonEquals($actualOutput, $expectedFileName)
+ public static function assertJsonEquals($actualOutput, $expectedFileName, $removeDynamicValues = true)
{
$actual = json_decode($actualOutput, true);
// Remove dynamic timestamp and duration attribute
- if (isset($actual['timestamp'])) {
- $actual['timestamp'] = '';
- }
- if (isset($actual['duration'])) {
- $actual['duration'] = '';
- }
- if (isset($actual['version'])) {
- $actual['version'] = '@package_version@';
+ if ($removeDynamicValues === true) {
+ if (isset($actual['timestamp'])) {
+ $actual['timestamp'] = '';
+ }
+ if (isset($actual['duration'])) {
+ $actual['duration'] = '';
+ }
+ if (isset($actual['version'])) {
+ $actual['version'] = '@package_version@';
+ }
+ } elseif ($removeDynamicValues instanceof Closure) {
+ $actual = $removeDynamicValues($actual);
}
$expected = str_replace(
@@ -173,6 +182,7 @@ public static function assertJsonEquals($actualOutput, $expectedFileName)
file_get_contents(self::createFileUri($expectedFileName))
);
+ $expected = str_replace('#{workingDirectory}', getcwd(), $expected);
$expected = str_replace('_DS_', DIRECTORY_SEPARATOR, $expected);
self::assertJsonStringEqualsJsonString($expected, json_encode($actual));
@@ -235,11 +245,17 @@ protected static function createFileUri($localPath = '')
/**
* Creates a file uri for a temporary test file.
*
+ * @param string|null $fileName
* @return string
*/
- protected static function createTempFileUri()
+ protected static function createTempFileUri($fileName = null)
{
- return (self::$tempFiles[] = tempnam(sys_get_temp_dir(), 'phpmd.'));
+ if ($fileName !== null) {
+ $filePath = sys_get_temp_dir() . '/' . $fileName;
+ } else {
+ $filePath = tempnam(sys_get_temp_dir(), 'phpmd.');
+ }
+ return (self::$tempFiles[] = $filePath);
}
/**
diff --git a/src/test/php/PHPMD/Baseline/BaselineFileFinderTest.php b/src/test/php/PHPMD/Baseline/BaselineFileFinderTest.php
new file mode 100644
index 000000000..97452c8d0
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineFileFinderTest.php
@@ -0,0 +1,78 @@
+find());
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ * @covers ::existingFile
+ */
+ public function testShouldFindExistingFileNearRuleSet()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('testA/phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+
+ // ensure consistent slashes
+ $expected = str_replace("\\", "/", realpath(static::createResourceUriForTest('testA/phpmd.baseline.xml')));
+ $actual = str_replace("\\", "/", $finder->existingFile()->find());
+
+ static::assertSame($expected, $actual);
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ */
+ public function testShouldReturnNullForNonExistingRuleSet()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+ static::assertNull($finder->find());
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ * @covers ::existingFile
+ */
+ public function testShouldOnlyFindExistingFile()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('testB/phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+ static::assertNull($finder->existingFile()->find());
+ }
+
+ /**
+ * @covers ::find
+ * @covers ::tryFind
+ * @covers ::notNull
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Unable to find the baseline file
+ */
+ public function testShouldThrowExceptionWhenFileIsNull()
+ {
+ $args = array('script', 'source', 'xml', static::createResourceUriForTest('testB/phpmd.xml'));
+ $finder = new BaselineFileFinder(new CommandLineOptions($args));
+ static::assertNull($finder->existingFile()->notNull()->find());
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/BaselineSetFactoryTest.php b/src/test/php/PHPMD/Baseline/BaselineSetFactoryTest.php
new file mode 100644
index 000000000..1db007f1e
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineSetFactoryTest.php
@@ -0,0 +1,81 @@
+contains('PHPMD\Rule\CleanCode\MissingImport', $baseDir . '/src/foo/bar', null));
+ static::assertTrue(
+ $set->contains('PHPMD\Rule\CleanCode\UndefinedVariable', $baseDir . '/src/foo/bar', 'myMethod')
+ );
+ }
+
+ /**
+ * @covers ::fromFile
+ */
+ public function testFromFileShouldSucceedWithBackAndForwardSlashes()
+ {
+ $filename = static::createResourceUriForTest('baseline.xml');
+ $baseDir = dirname($filename);
+ $set = BaselineSetFactory::fromFile($filename);
+
+ static::assertTrue($set->contains('PHPMD\Rule\CleanCode\MissingImport', $baseDir . '/src\\foo/bar', null));
+ static::assertTrue(
+ $set->contains('PHPMD\Rule\CleanCode\UndefinedVariable', $baseDir . '/src\\foo/bar', 'myMethod')
+ );
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Unable to locate the baseline file at
+ */
+ public function testFromFileShouldThrowExceptionForMissingFile()
+ {
+ BaselineSetFactory::fromFile('foobar.xml');
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Unable to read xml from
+ */
+ public function testFromFileShouldThrowExceptionForOnInvalidXML()
+ {
+ BaselineSetFactory::fromFile(static::createResourceUriForTest('invalid-baseline.xml'));
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Missing `rule` attribute in `violation`
+ */
+ public function testFromFileViolationMissingRuleShouldThrowException()
+ {
+ BaselineSetFactory::fromFile(static::createResourceUriForTest('missing-rule-baseline.xml'));
+ }
+
+ /**
+ * @covers ::fromFile
+ * @expectedException \RuntimeException
+ * @expectedExceptionMessage Missing `file` attribute in `violation` in
+ */
+ public function testFromFileViolationMissingFileShouldThrowException()
+ {
+ BaselineSetFactory::fromFile(static::createResourceUriForTest('missing-file-baseline.xml'));
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/BaselineSetTest.php b/src/test/php/PHPMD/Baseline/BaselineSetTest.php
new file mode 100644
index 000000000..f3a7d8073
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineSetTest.php
@@ -0,0 +1,76 @@
+addEntry(new ViolationBaseline('rule', 'foobar', null));
+
+ static::assertTrue($set->contains('rule', 'foobar', null));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testSetContainsEntryWithMethodName()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foobar', 'method'));
+
+ static::assertTrue($set->contains('rule', 'foobar', 'method'));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testShouldFindEntryForIdenticalRules()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foo', null));
+ $set->addEntry(new ViolationBaseline('rule', 'bar', null));
+ $set->addEntry(new ViolationBaseline('rule', 'bar', 'method'));
+
+ static::assertTrue($set->contains('rule', 'foo', null));
+ static::assertTrue($set->contains('rule', 'bar', null));
+ static::assertTrue($set->contains('rule', 'bar', 'method'));
+ static::assertFalse($set->contains('rule', 'unknown', null));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testShouldNotFindEntryForNonExistingRule()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foo', null));
+
+ static::assertFalse($set->contains('unknown', 'foo', null));
+ }
+
+ /**
+ * @covers ::addEntry
+ * @covers ::contains
+ */
+ public function testShouldNotFindEntryForNonExistingMethod()
+ {
+ $set = new BaselineSet();
+ $set->addEntry(new ViolationBaseline('rule', 'foo', 'method'));
+
+ static::assertFalse($set->contains('rule', 'foo', 'unknown'));
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/BaselineValidatorTest.php b/src/test/php/PHPMD/Baseline/BaselineValidatorTest.php
new file mode 100644
index 000000000..12caac3b4
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/BaselineValidatorTest.php
@@ -0,0 +1,66 @@
+getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\Rule')->disableOriginalConstructor()
+ );
+ $this->violation = $this->getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\RuleViolation')->disableOriginalConstructor()
+ );
+ $this->violation
+ ->method('getRule')
+ ->willReturn($rule);
+ $this->baselineSet = $this->getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\Baseline\BaselineSet')->disableOriginalConstructor()
+ );
+ }
+
+ /**
+ * @covers ::isBaselined
+ * @dataProvider dataProvider
+ * @param bool $contains
+ * @param string $baselineMode
+ * @param bool $isBaselined
+ */
+ public function testIsBaselined($contains, $baselineMode, $isBaselined)
+ {
+ $this->baselineSet->method('contains')->willReturn($contains);
+ $validator = new BaselineValidator($this->baselineSet, $baselineMode);
+ static::assertSame($isBaselined, $validator->isBaselined($this->violation));
+ }
+
+ /**
+ * @return array
+ */
+ public function dataProvider()
+ {
+ return array(
+ 'contains: true, mode: none' => array(true, BaselineMode::NONE, true),
+ 'contains: false, mode: none' => array(false, BaselineMode::NONE, false),
+ 'contains: true, mode: update' => array(true, BaselineMode::UPDATE, false),
+ 'contains: false, mode: update' => array(false, BaselineMode::UPDATE, true),
+ 'contains: true, mode: generate' => array(true, BaselineMode::GENERATE, false),
+ 'contains: false, mode: generate' => array(false, BaselineMode::GENERATE, false),
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Baseline/ViolationBaselineTest.php b/src/test/php/PHPMD/Baseline/ViolationBaselineTest.php
new file mode 100644
index 000000000..ae8b4bf04
--- /dev/null
+++ b/src/test/php/PHPMD/Baseline/ViolationBaselineTest.php
@@ -0,0 +1,35 @@
+getRuleName());
+ static::assertSame('foobar', $violation->getFileName());
+ static::assertNull($violation->getMethodName());
+ }
+
+ /**
+ * @covers ::__construct
+ * @covers ::getMethodName
+ */
+ public function testAccessorsWithMethod()
+ {
+ $violation = new ViolationBaseline('rule', 'foobar', 'method');
+ static::assertSame('method', $violation->getMethodName());
+ }
+}
diff --git a/src/test/php/PHPMD/PHPMDTest.php b/src/test/php/PHPMD/PHPMDTest.php
index 11a2ec6d4..c9d01b1f6 100644
--- a/src/test/php/PHPMD/PHPMDTest.php
+++ b/src/test/php/PHPMD/PHPMDTest.php
@@ -9,16 +9,20 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD;
+use PHPMD\Baseline\BaselineMode;
+use PHPMD\Baseline\BaselineSet;
+use PHPMD\Baseline\BaselineValidator;
use PHPMD\Renderer\XMLRenderer;
use PHPMD\Stubs\WriterStub;
+use PHPUnit_Framework_MockObject_MockObject;
/**
* Test case for the main PHPMD class.
@@ -47,7 +51,8 @@ public function testRunWithDefaultSettingsAndXmlRenderer()
self::createFileUri('source/ccn_function.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
$this->assertXmlEquals($writer->getData(), 'pmd/default-xml.xml');
@@ -72,7 +77,8 @@ public function testRunWithDefaultSettingsAndXmlRendererAgainstDirectory()
self::createFileUri('source'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
$this->assertXmlEquals($writer->getData(), 'pmd/single-directory.xml');
@@ -97,12 +103,24 @@ public function testRunWithDefaultSettingsAndXmlRendererAgainstSingleFile()
self::createFileUri('source/ccn_function.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
$this->assertXmlEquals($writer->getData(), 'pmd/single-file.xml');
}
+ /**
+ * testHasErrorsReturnsFalseByDefault
+ *
+ * @return void
+ */
+ public function testHasErrorsReturnsFalseByDefault()
+ {
+ $phpmd = new PHPMD();
+ $this->assertFalse($phpmd->hasErrors());
+ }
+
/**
* testHasViolationsReturnsFalseByDefault
*
@@ -131,9 +149,11 @@ public function testHasViolationsReturnsFalseForSourceWithoutViolations()
self::createFileUri('source/source_without_violations.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertFalse($phpmd->hasViolations());
}
@@ -154,12 +174,67 @@ public function testHasViolationsReturnsTrueForSourceWithViolation()
self::createFileUri('source/source_with_npath_violation.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertTrue($phpmd->hasViolations());
}
+ /**
+ * @return void
+ */
+ public function testHasViolationsReturnsFalseWhenViolationIsBaselined()
+ {
+ self::changeWorkingDirectory();
+
+ /** @var BaselineSet|PHPUnit_Framework_MockObject_MockObject $baselineSet */
+ $baselineSet = $this->getMockFromBuilder(
+ $this->getMockBuilder('\PHPMD\Baseline\BaselineSet')->disableOriginalConstructor()
+ );
+ $baselineSet->expects(static::exactly(2))->method('contains')->willReturn(true);
+
+ $renderer = new XMLRenderer();
+ $renderer->setWriter(new WriterStub());
+
+ $phpmd = new PHPMD();
+ $phpmd->processFiles(
+ self::createFileUri('source/source_with_npath_violation.php'),
+ 'pmd-refset1',
+ array($renderer),
+ new RuleSetFactory(),
+ new Report(new BaselineValidator($baselineSet, BaselineMode::NONE))
+ );
+
+ static::assertFalse($phpmd->hasViolations());
+ }
+
+ /**
+ * testHasErrorsReturnsTrueForSourceWithError
+ *
+ * @return void
+ */
+ public function testHasErrorsReturnsTrueForSourceWithError()
+ {
+ self::changeWorkingDirectory();
+
+ $renderer = new XMLRenderer();
+ $renderer->setWriter(new WriterStub());
+
+ $phpmd = new PHPMD();
+ $phpmd->processFiles(
+ self::createFileUri('source/source_with_parse_error.php'),
+ 'pmd-refset1',
+ array($renderer),
+ new RuleSetFactory(),
+ new Report()
+ );
+
+ $this->assertTrue($phpmd->hasErrors());
+ $this->assertFalse($phpmd->hasViolations());
+ }
+
/**
* testIgnorePattern
*
@@ -176,9 +251,11 @@ public function testIgnorePattern()
self::createFileUri('sourceExcluded/'),
'pmd-refset1',
array(),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertTrue($phpmd->hasViolations());
// Process with exclusions, should result in no violations.
@@ -186,9 +263,11 @@ public function testIgnorePattern()
self::createFileUri('sourceExcluded/'),
'exclude-pattern',
array(),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
+ $this->assertFalse($phpmd->hasErrors());
$this->assertFalse($phpmd->hasViolations());
}
}
diff --git a/src/test/php/PHPMD/ParserFactoryTest.php b/src/test/php/PHPMD/ParserFactoryTest.php
index 2de4b2ddb..2ec7a5b80 100644
--- a/src/test/php/PHPMD/ParserFactoryTest.php
+++ b/src/test/php/PHPMD/ParserFactoryTest.php
@@ -140,10 +140,10 @@ public function testFactoryConfiguresIgnorePattern()
$phpmd = $this->getMockFromBuilder(
$this->getMockBuilder('PHPMD\\PHPMD')
- ->setMethods(array('getIgnorePattern', 'getInput'))
+ ->setMethods(array('getIgnorePatterns', 'getInput'))
);
$phpmd->expects($this->exactly(2))
- ->method('getIgnorePattern')
+ ->method('getIgnorePatterns')
->will($this->returnValue(array('Test')));
$phpmd->expects($this->once())
->method('getInput')
diff --git a/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php b/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php
index c8898be7a..9c1c913f1 100644
--- a/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php
+++ b/src/test/php/PHPMD/Regression/AcceptsFilesAndDirectoriesAsInputTicket001Test.php
@@ -19,6 +19,7 @@
use PHPMD\PHPMD;
use PHPMD\Renderer\XMLRenderer;
+use PHPMD\Report;
use PHPMD\RuleSetFactory;
use PHPMD\Stubs\WriterStub;
@@ -46,7 +47,8 @@ public function testCliAcceptsDirectoryAsInput()
self::createFileUri('source'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
@@ -67,7 +69,8 @@ public function testCliAcceptsSingleFileAsInput()
self::createFileUri('source/FooBar.php'),
'pmd-refset1',
array($renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
}
diff --git a/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php b/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php
index 30a0bb32c..5c2ccf14a 100644
--- a/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php
+++ b/src/test/php/PHPMD/Regression/ExcessivePublicCountWorksCorrectlyWithStaticMethodsTest.php
@@ -87,7 +87,8 @@ function (Report $report) use ($self) {
__DIR__ . '/Sources/ExcessivePublicCountWorksForPublicStaticMethods.php',
'codesize',
array($this->renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
@@ -127,7 +128,8 @@ function (Report $report) use ($self) {
__DIR__ . '/Sources/ExcessivePublicCountSuppressionWorksForPublicStaticMethods.php',
'codesize',
array($this->renderer),
- new RuleSetFactory()
+ new RuleSetFactory(),
+ new Report()
);
}
}
diff --git a/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php b/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php
index 7afa4d7f6..10bdc932c 100644
--- a/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php
+++ b/src/test/php/PHPMD/Regression/MaximumNestingLevelTicket24975295Test.php
@@ -19,6 +19,7 @@
use PHPMD\PHPMD;
use PHPMD\Renderer\TextRenderer;
+use PHPMD\Report;
use PHPMD\RuleSetFactory;
use PHPMD\Writer\StreamWriter;
@@ -49,6 +50,6 @@ public function testLocalVariableUsedInDoubleQuoteStringGetsNotReported()
$factory = new RuleSetFactory();
$phpmd = new PHPMD();
- $phpmd->processFiles($inputs, $rules, $renderes, $factory);
+ $phpmd->processFiles($inputs, $rules, $renderes, $factory, new Report());
}
}
diff --git a/src/test/php/PHPMD/Renderer/BaselineRendererTest.php b/src/test/php/PHPMD/Renderer/BaselineRendererTest.php
new file mode 100644
index 000000000..3d6c8fd9f
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/BaselineRendererTest.php
@@ -0,0 +1,124 @@
+getRuleViolationMock('/src/php/bar.php'),
+ $this->getRuleViolationMock('/src/php/foo.php'),
+ );
+
+ /** @var Report|MockObject $report */
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator($violations));
+
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected1.xml'
+ );
+ }
+
+ /**
+ * @covers ::renderReport
+ */
+ public function testRenderReportShouldWriteMethodName()
+ {
+ $writer = new WriterStub();
+ $violationMock = $this->getRuleViolationMock('/src/php/bar.php');
+ $violationMock->expects(static::once())->method('getMethodName')->willReturn('foo');
+
+ /** @var Report|MockObject $report */
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator(array($violationMock)));
+
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected2.xml'
+ );
+ }
+
+ /**
+ * @covers ::renderReport
+ */
+ public function testRenderReportShouldDeduplicateSimilarViolations()
+ {
+ $writer = new WriterStub();
+ $violationMock = $this->getRuleViolationMock('/src/php/bar.php');
+ $violationMock->expects(static::exactly(2))->method('getMethodName')->willReturn('foo');
+
+ // add the same violation twice
+ /** @var Report|MockObject $report */
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator(array($violationMock, $violationMock)));
+
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected2.xml'
+ );
+ }
+
+ /**
+ * @covers ::renderReport
+ */
+ public function testRenderEmptyReport()
+ {
+ $writer = new WriterStub();
+ $report = $this->getReportWithNoViolation();
+ $report->expects(static::once())
+ ->method('getRuleViolations')
+ ->willReturn(new ArrayIterator(array()));
+
+ /** @var Report|MockObject $report */
+ $renderer = new BaselineRenderer('/src');
+ $renderer->setWriter($writer);
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ static::assertXmlEquals(
+ $writer->getData(),
+ 'renderer/baseline_renderer_expected3.xml'
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/CheckStyleRendererTest.php b/src/test/php/PHPMD/Renderer/CheckStyleRendererTest.php
new file mode 100644
index 000000000..16e6f2777
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/CheckStyleRendererTest.php
@@ -0,0 +1,105 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\AbstractTest;
+use PHPMD\ProcessingError;
+use PHPMD\Stubs\WriterStub;
+
+/**
+ * Test case for the xml renderer implementation.
+ *
+ * @covers \PHPMD\Renderer\XMLRenderer
+ */
+class CheckStyleRendererTest extends AbstractTest
+{
+ /**
+ * testRendererCreatesExpectedNumberOfXmlElements
+ *
+ * @return void
+ */
+ public function testRendererCreatesExpectedNumberOfXmlElements()
+ {
+ // Create a writer instance.
+ $writer = new WriterStub();
+
+ $violations = array(
+ $this->getRuleViolationMock('/bar.php'),
+ $this->getRuleViolationMock('/foo.php'),
+ $this->getRuleViolationMock('/foo.php', 23, 42, null, 'foo getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator($violations)));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+
+ $renderer = new XMLRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertXmlEquals(
+ $writer->getData(),
+ 'renderer/xml_renderer_expected1.xml'
+ );
+ }
+
+ /**
+ * testRendererAddsProcessingErrorsToXmlReport
+ *
+ * @return void
+ * @since 1.2.1
+ */
+ public function testRendererAddsProcessingErrorsToXmlReport()
+ {
+ // Create a writer instance.
+ $writer = new WriterStub();
+
+ $processingErrors = array(
+ new ProcessingError('Failed for file "/tmp/foo.php".'),
+ new ProcessingError('Failed for file "/tmp/bar.php".'),
+ new ProcessingError('Failed for file "/tmp/baz.php".'),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator($processingErrors)));
+
+ $renderer = new XMLRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertXmlEquals(
+ $writer->getData(),
+ 'renderer/xml_renderer_processing_errors.xml'
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/GitHubRendererTest.php b/src/test/php/PHPMD/Renderer/GitHubRendererTest.php
new file mode 100644
index 000000000..4cc4ac3e2
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/GitHubRendererTest.php
@@ -0,0 +1,108 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Lukas Bestle
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\AbstractTest;
+use PHPMD\ProcessingError;
+use PHPMD\Stubs\WriterStub;
+
+/**
+ * Test case for the GitHub renderer implementation.
+ *
+ * @covers \PHPMD\Renderer\GitHubRenderer
+ */
+class GitHubRendererTest extends AbstractTest
+{
+ /**
+ * testRendererCreatesExpectedNumberOfTextEntries
+ *
+ * @return void
+ */
+ public function testRendererCreatesExpectedNumberOfTextEntries()
+ {
+ // Create a writer instance.
+ $writer = new WriterStub();
+
+ $violations = array(
+ $this->getRuleViolationMock('/bar.php', 1),
+ $this->getRuleViolationMock('/foo.php', 2),
+ $this->getRuleViolationMock('/foo.php', 3),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator($violations)));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+
+ $renderer = new GitHubRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertEquals(
+ "::warning file=/bar.php,line=1::Test description" . PHP_EOL .
+ "::warning file=/foo.php,line=2::Test description" . PHP_EOL .
+ "::warning file=/foo.php,line=3::Test description" . PHP_EOL,
+ $writer->getData()
+ );
+ }
+
+ /**
+ * testRendererAddsProcessingErrorsToTextReport
+ *
+ * @return void
+ */
+ public function testRendererAddsProcessingErrorsToTextReport()
+ {
+ // Create a writer instance.
+ $writer = new WriterStub();
+
+ $errors = array(
+ new ProcessingError('Failed for file "/tmp/foo.php".'),
+ new ProcessingError('Failed for file "/tmp/bar.php".'),
+ new ProcessingError('Failed for file "/tmp/baz.php".'),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator($errors)));
+
+ $renderer = new GitHubRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertEquals(
+ "::error file=/tmp/foo.php::Failed for file \"/tmp/foo.php\"." . PHP_EOL .
+ "::error file=/tmp/bar.php::Failed for file \"/tmp/bar.php\"." . PHP_EOL .
+ "::error file=/tmp/baz.php::Failed for file \"/tmp/baz.php\"." . PHP_EOL,
+ $writer->getData()
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/RendererFactoryTest.php b/src/test/php/PHPMD/Renderer/RendererFactoryTest.php
new file mode 100644
index 000000000..e2707db8f
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/RendererFactoryTest.php
@@ -0,0 +1,35 @@
+getWriter());
+ }
+
+ /**
+ * @covers ::createBaselineRenderer
+ * @expectedException RuntimeException
+ * @expectedExceptionMessage Unable to determine the realpath for
+ */
+ public function testCreateBaselineRendererThrowsExceptionForInvalidStream()
+ {
+ $writer = new StreamWriter(STDOUT);
+ RendererFactory::createBaselineRenderer($writer);
+ }
+}
diff --git a/src/test/php/PHPMD/Renderer/SARIFRendererTest.php b/src/test/php/PHPMD/Renderer/SARIFRendererTest.php
new file mode 100644
index 000000000..0d54b45fd
--- /dev/null
+++ b/src/test/php/PHPMD/Renderer/SARIFRendererTest.php
@@ -0,0 +1,122 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Lukas Bestle
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMD\Renderer;
+
+use PHPMD\AbstractTest;
+use PHPMD\ProcessingError;
+use PHPMD\Stubs\RuleStub;
+use PHPMD\Stubs\WriterStub;
+
+/**
+ * Test case for the SARIF renderer implementation.
+ *
+ * @covers \PHPMD\Renderer\SARIFRenderer
+ */
+class SARIFRendererTest extends AbstractTest
+{
+ /**
+ * testRendererCreatesExpectedNumberOfJsonElements
+ *
+ * @return void
+ */
+ public function testRendererCreatesExpectedNumberOfJsonElements()
+ {
+ $writer = new WriterStub();
+
+ $rule = new RuleStub('AnotherRuleStub');
+ $rule->addExample(" class Example\n{\n}\n ");
+ $rule->addExample("\nclass AnotherExample\n{\n public \$var;\n}\n ");
+ $rule->setSince(null);
+
+ $complexRuleViolationMock = $this->getRuleViolationMock(getcwd() . '/src/foobar.php', 23, 42, $rule);
+ $complexRuleViolationMock
+ ->method('getArgs')
+ ->willReturn(array(123, 3.2, 'awesomeFunction()'));
+
+ $violations = array(
+ $this->getRuleViolationMock('/bar.php'),
+ $this->getRuleViolationMock('/foo.php'),
+ $complexRuleViolationMock,
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator($violations)));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+
+ $renderer = new SARIFRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertJsonEquals(
+ $writer->getData(),
+ 'renderer/sarif_renderer_expected.sarif',
+ function ($actual) {
+ $actual['runs'][0]['tool']['driver']['version'] = '@package_version@';
+ return $actual;
+ }
+ );
+ }
+
+ /**
+ * testRendererAddsProcessingErrorsToJsonReport
+ *
+ * @return void
+ */
+ public function testRendererAddsProcessingErrorsToJsonReport()
+ {
+ $writer = new WriterStub();
+
+ $processingErrors = array(
+ new ProcessingError('Failed for file "/tmp/foo.php".'),
+ new ProcessingError('Failed for file "/tmp/bar.php".'),
+ new ProcessingError('Failed for file "' . static::createFileUri('foobar.php') . '".'),
+ new ProcessingError('Cannot read file "/tmp/foo.php". Permission denied.'),
+ );
+
+ $report = $this->getReportWithNoViolation();
+ $report->expects($this->once())
+ ->method('getRuleViolations')
+ ->will($this->returnValue(new \ArrayIterator(array())));
+ $report->expects($this->once())
+ ->method('getErrors')
+ ->will($this->returnValue(new \ArrayIterator($processingErrors)));
+
+ $renderer = new SARIFRenderer();
+ $renderer->setWriter($writer);
+
+ $renderer->start();
+ $renderer->renderReport($report);
+ $renderer->end();
+
+ $this->assertJsonEquals(
+ $writer->getData(),
+ 'renderer/sarif_renderer_processing_errors.sarif',
+ function ($actual) {
+ $actual['runs'][0]['tool']['driver']['version'] = '@package_version@';
+ return $actual;
+ }
+ );
+ }
+}
diff --git a/src/test/php/PHPMD/ReportTest.php b/src/test/php/PHPMD/ReportTest.php
index 7944a1c19..2ce027427 100644
--- a/src/test/php/PHPMD/ReportTest.php
+++ b/src/test/php/PHPMD/ReportTest.php
@@ -9,14 +9,19 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD;
+use PHPMD\Baseline\BaselineMode;
+use PHPMD\Baseline\BaselineSet;
+use PHPMD\Baseline\BaselineValidator;
+use PHPMD\Baseline\ViolationBaseline;
+
/**
* Test case for the report class.
*
@@ -185,4 +190,56 @@ public function testGetErrorsReturnsPreviousAddedProcessingError()
$this->assertSame(1, iterator_count($report->getErrors()));
}
+
+ /**
+ * @return void
+ */
+ public function testReportShouldIgnoreBaselineViolation()
+ {
+ /** @var RuleViolation $ruleA */
+ $ruleA = $this->getRuleViolationMock('foo.txt');
+ /** @var RuleViolation $ruleB */
+ $ruleB = $this->getRuleViolationMock('bar.txt', 1, 2);
+
+ // setup baseline
+ $violation = new ViolationBaseline(get_class($ruleA->getRule()), 'foo.txt', null);
+ $baseline = new BaselineSet();
+ $baseline->addEntry($violation);
+
+ // setup report
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::NONE));
+ $report->addRuleViolation($ruleA);
+ $report->addRuleViolation($ruleB);
+
+ // only expect ruleB
+ $violations = $report->getRuleViolations();
+ static::assertCount(1, $violations);
+ static::assertSame($ruleB, $violations[0]);
+ }
+
+ /**
+ * @return void
+ */
+ public function testReportShouldIgnoreNewViolationsOnBaselineUpdate()
+ {
+ /** @var RuleViolation $ruleA */
+ $ruleA = $this->getRuleViolationMock('foo.txt');
+ /** @var RuleViolation $ruleB */
+ $ruleB = $this->getRuleViolationMock('bar.txt', 1, 2);
+
+ // setup baseline
+ $violation = new ViolationBaseline(get_class($ruleA->getRule()), 'foo.txt', null);
+ $baseline = new BaselineSet();
+ $baseline->addEntry($violation);
+
+ // setup report
+ $report = new Report(new BaselineValidator($baseline, BaselineMode::UPDATE));
+ $report->addRuleViolation($ruleA);
+ $report->addRuleViolation($ruleB);
+
+ // only expect ruleA, as ruleB is new and should not be in the report.
+ $violations = $report->getRuleViolations();
+ static::assertCount(1, $violations);
+ static::assertSame($ruleA, $violations[0]);
+ }
}
diff --git a/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php b/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php
index 2aca7b62c..c6597d1c7 100644
--- a/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php
+++ b/src/test/php/PHPMD/Rule/CleanCode/MissingImportTest.php
@@ -33,7 +33,9 @@ class MissingImportTest extends AbstractTest
*/
public function getRule()
{
- return new MissingImport();
+ $rule = new MissingImport();
+ $rule->addProperty('ignore-global', false);
+ return $rule;
}
/**
@@ -69,8 +71,22 @@ public function testRuleDoesNotApplyTo($file)
*/
public function testRuleAppliesTwiceToClassWithNotImportedDependencies()
{
- $rule = new MissingImport();
+ $rule = $this->getRule();
$rule->setReport($this->getReportMock(2));
$rule->apply($this->getMethod());
}
+
+ /**
+ * Tests the rule ignores classes in global namespace with `ignore-global`.
+ *
+ * @param string $file The test file to test against.
+ * @return void
+ * @dataProvider getApplyingCases
+ */
+ public function testRuleDoesNotApplyWithIgnoreGlobalProperty($file)
+ {
+ $rule = $this->getRule();
+ $rule->addProperty('ignore-global', true);
+ $this->expectRuleHasViolationsForFile($rule, static::NO_VIOLATION, $file);
+ }
}
diff --git a/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php b/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php
index 87bfa8c51..eae6e2ca8 100644
--- a/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php
+++ b/src/test/php/PHPMD/Rule/Controversial/CamelCaseMethodNameTest.php
@@ -158,7 +158,7 @@ public function testRuleDoesApplyForTestMethodWithUnderscoreWhenNotAllowed()
/**
* Tests that the rule does not apply for a valid test method name
- * with an underscore when an single underscore is allowed.
+ * with an underscore when underscores are allowed.
*
* @return void
*/
@@ -174,13 +174,31 @@ public function testRuleDoesNotApplyForTestMethodWithUnderscoreWhenAllowed()
$rule->apply($method);
}
+ /**
+ * Tests that the rule does not apply for a valid test method name
+ * with multiple underscores in different positions when underscores are allowed.
+ *
+ * @return void
+ */
+ public function testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed()
+ {
+ $method = $this->getMethod();
+ $report = $this->getReportWithNoViolation();
+
+ $rule = new CamelCaseMethodName();
+ $rule->setReport($report);
+ $rule->addProperty('allow-underscore', 'false');
+ $rule->addProperty('allow-underscore-test', 'true');
+ $rule->apply($method);
+ }
+
/**
* Tests that the rule does apply for a test method name
- * with multiple underscores even when one is allowed.
+ * with consecutive underscores even when underscores are allowed.
*
* @return void
*/
- public function testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed()
+ public function testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed()
{
$method = $this->getMethod();
$report = $this->getReportWithOneViolation();
diff --git a/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php b/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php
index dc0183a78..91025ea86 100644
--- a/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php
+++ b/src/test/php/PHPMD/Rule/Design/TooManyPublicMethodsTest.php
@@ -21,6 +21,7 @@
use PDepend\Source\AST\State;
use PHPMD\AbstractTest;
use PHPMD\Node\MethodNode;
+use PHPMD\Report;
/**
* Test case for the too many public methods rule.
@@ -161,6 +162,22 @@ public function testRuleIgnoresWithers()
$rule->apply($this->createClassMock(2, array('invoke', 'withClass')));
}
+ public function testRuleApplyToBasicClass()
+ {
+ $class = $this->getClass();
+ $rule = new TooManyPublicMethods();
+ $report = new Report();
+ $rule->setReport($report);
+ $rule->addProperty('maxmethods', '5');
+ $rule->addProperty('ignorepattern', '');
+ $rule->apply($class);
+ $violations = $report->getRuleViolations();
+
+ $this->assertCount(1, $violations);
+
+ $this->assertSame(6, $violations[0]->getBeginLine());
+ }
+
/**
* Creates a prepared class node mock
*
diff --git a/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php b/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php
index fb744843b..db5f5dc7d 100644
--- a/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php
+++ b/src/test/php/PHPMD/Rule/Naming/ShortVariableTest.php
@@ -41,6 +41,20 @@ public function testRuleAppliesToLocalVariableInFunctionWithNameShorterThanThres
$rule->apply($this->getFunction());
}
+ /**
+ * testRuleAppliesToTryCatchBlocks
+ *
+ * @return void
+ */
+ public function testRuleNotAppliesToTryCatchBlocksInsideForeach()
+ {
+ $rule = new ShortVariable();
+ $rule->addProperty('minimum', 3);
+ $rule->addProperty('exceptions', '');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getFunction());
+ }
+
/**
* testRuleNotAppliesToLocalVariableInFunctionWithNameLongerThanThreshold
*
diff --git a/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php b/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php
index 09923ae1a..f8fdc900e 100644
--- a/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php
+++ b/src/test/php/PHPMD/Rule/UnusedLocalVariableTest.php
@@ -637,6 +637,28 @@ public function testRuleDoesNotApplyToCompactFunction()
$rule->apply($this->getMethod());
}
+ /**
+ * testRuleDoesNotApplyToCompactWithDoubleQuotesFunction
+ *
+ *
+ * class Foo {
+ * public function bar() {
+ * $key = "ok";
+ * return compact("key");
+ * }
+ * }
+ *
+ *
+ * @return void
+ */
+ public function testRuleDoesNotApplyToCompactWithDoubleQuotesFunction()
+ {
+ $rule = new UnusedLocalVariable();
+ $rule->addProperty('allow-unused-foreach-variables', 'false');
+ $rule->setReport($this->getReportWithNoViolation());
+ $rule->apply($this->getMethod());
+ }
+
/**
* @test
* @return void
diff --git a/src/test/php/PHPMD/Stubs/RuleStub.php b/src/test/php/PHPMD/Stubs/RuleStub.php
index 14f35e746..87624be94 100644
--- a/src/test/php/PHPMD/Stubs/RuleStub.php
+++ b/src/test/php/PHPMD/Stubs/RuleStub.php
@@ -41,6 +41,7 @@ public function __construct($ruleName = 'RuleStub', $ruleSetName = 'TestRuleSet'
$this->setRuleSetName($ruleSetName);
$this->setSince('42.23');
$this->setDescription('Simple rule stub');
+ $this->setMessage('Test description');
}
/**
diff --git a/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php b/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php
index a17cf7a9a..c03c2eed4 100644
--- a/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php
+++ b/src/test/php/PHPMD/TextUI/CommandLineOptionsTest.php
@@ -18,6 +18,7 @@
namespace PHPMD\TextUI;
use PHPMD\AbstractTest;
+use PHPMD\Baseline\BaselineMode;
use PHPMD\Rule;
/**
@@ -187,6 +188,45 @@ public function testCliOptionsAcceptsVersionArgument()
self::assertTrue($opts->hasVersion());
}
+ /**
+ * Tests if ignoreErrorsOnExit returns false by default
+ *
+ * @return void
+ */
+ public function testIgnoreErrorsOnExitReturnsFalseByDefault()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'unusedcode');
+ $opts = new CommandLineOptions($args);
+
+ self::assertFalse($opts->ignoreErrorsOnExit());
+ }
+
+ /**
+ * Tests if CLI options accepts ignoreErrorsOnExit argument
+ *
+ * @return void
+ */
+ public function testCliOptionsAcceptsIgnoreErrorsOnExitArgument()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'unusedcode', '--ignore-errors-on-exit');
+ $opts = new CommandLineOptions($args);
+
+ self::assertTrue($opts->ignoreErrorsOnExit());
+ }
+
+ /**
+ * Tests if CLI usage contains ignoreErrorsOnExit option
+ *
+ * @return void
+ */
+ public function testCliUsageContainsIgnoreErrorsOnExitOption()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize');
+ $opts = new CommandLineOptions($args);
+
+ $this->assertContains('--ignore-errors-on-exit:', $opts->usage());
+ }
+
/**
* Tests if ignoreViolationsOnExit returns false by default
*
@@ -236,7 +276,10 @@ public function testCliUsageContainsAutoDiscoveredRenderers()
$args = array(__FILE__, __FILE__, 'text', 'codesize');
$opts = new CommandLineOptions($args);
- $this->assertContains('Available formats: ansi, html, json, text, xml.', $opts->usage());
+ $this->assertContains(
+ 'Available formats: ansi, baseline, checkstyle, github, html, json, sarif, text, xml.',
+ $opts->usage()
+ );
}
/**
@@ -302,6 +345,56 @@ public function testCliOptionsAcceptsMaximumpriorityArgument()
$this->assertEquals(42, $opts->getMaximumPriority());
}
+ /**
+ * @return void
+ */
+ public function testCliOptionGenerateBaselineFalseByDefault()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize');
+ $opts = new CommandLineOptions($args);
+ static::assertSame(BaselineMode::NONE, $opts->generateBaseline());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionGenerateBaselineShouldBeSet()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize', '--generate-baseline');
+ $opts = new CommandLineOptions($args);
+ static::assertSame(BaselineMode::GENERATE, $opts->generateBaseline());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionUpdateBaselineShouldBeSet()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize', '--update-baseline');
+ $opts = new CommandLineOptions($args);
+ static::assertSame(BaselineMode::UPDATE, $opts->generateBaseline());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionBaselineFileShouldBeNullByDefault()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize');
+ $opts = new CommandLineOptions($args);
+ static::assertNull($opts->baselineFile());
+ }
+
+ /**
+ * @return void
+ */
+ public function testCliOptionBaselineFileShouldBeWithFilename()
+ {
+ $args = array(__FILE__, __FILE__, 'text', 'codesize', '--baseline-file', 'foobar.txt');
+ $opts = new CommandLineOptions($args);
+ static::assertSame('foobar.txt', $opts->baselineFile());
+ }
+
/**
* @return void
*/
diff --git a/src/test/php/PHPMD/TextUI/CommandTest.php b/src/test/php/PHPMD/TextUI/CommandTest.php
index 6a1ff779b..dfa789872 100644
--- a/src/test/php/PHPMD/TextUI/CommandTest.php
+++ b/src/test/php/PHPMD/TextUI/CommandTest.php
@@ -9,10 +9,10 @@
* For full copyright and license information, please see the LICENSE file.
* Redistributions of files must retain the above copyright notice.
*
- * @author Manuel Pichler
+ * @author Manuel Pichler
* @copyright Manuel Pichler. All rights reserved.
- * @license https://opensource.org/licenses/bsd-license.php BSD License
- * @link http://phpmd.org/
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
*/
namespace PHPMD\TextUI;
@@ -45,8 +45,8 @@ protected function tearDown()
}
/**
- * @param $sourceFile
- * @param $expectedExitCode
+ * @param $sourceFile
+ * @param $expectedExitCode
* @param array|null $options
* @return void
* @dataProvider dataProviderTestMainWithOption
@@ -90,6 +90,44 @@ public function dataProviderTestMainWithOption()
Command::EXIT_SUCCESS,
array('--ignore-violations-on-exit'),
),
+ array(
+ 'source/source_with_npath_violation.php',
+ Command::EXIT_VIOLATION,
+ array('--ignore-errors-on-exit'),
+ ),
+ array(
+ 'source/source_with_parse_error.php',
+ Command::EXIT_ERROR,
+ ),
+ array(
+ 'source/source_with_parse_error.php',
+ Command::EXIT_ERROR,
+ array('--ignore-violations-on-exit'),
+ ),
+ array(
+ 'source/source_with_parse_error.php',
+ Command::EXIT_SUCCESS,
+ array('--ignore-errors-on-exit'),
+ ),
+ array(
+ 'source',
+ Command::EXIT_ERROR,
+ ),
+ array(
+ 'source',
+ Command::EXIT_ERROR,
+ array('--ignore-violations-on-exit'),
+ ),
+ array(
+ 'source',
+ Command::EXIT_VIOLATION,
+ array('--ignore-errors-on-exit'),
+ ),
+ array(
+ 'source',
+ Command::EXIT_SUCCESS,
+ array('--ignore-errors-on-exit', '--ignore-violations-on-exit'),
+ ),
array(
'source/ccn_suppress_function.php',
Command::EXIT_VIOLATION,
@@ -122,6 +160,10 @@ public function testWithMultipleReportFiles()
$text = self::createTempFileUri(),
'--reportfile-json',
$json = self::createTempFileUri(),
+ '--reportfile-checkstyle',
+ $checkstyle = self::createTempFileUri(),
+ '--reportfile-sarif',
+ $sarif = self::createTempFileUri(),
);
Command::main($args);
@@ -130,12 +172,14 @@ public function testWithMultipleReportFiles()
$this->assertFileExists($html);
$this->assertFileExists($text);
$this->assertFileExists($json);
+ $this->assertFileExists($checkstyle);
+ $this->assertFileExists($sarif);
}
public function testOutput()
{
- $uri = realpath(self::createFileUri('source/source_with_anonymous_class.php'));
- $temp = self::createTempFileUri();
+ $uri = realpath(self::createFileUri('source/source_with_anonymous_class.php'));
+ $temp = self::createTempFileUri();
$exitCode = Command::main(array(
__FILE__,
$uri,
@@ -182,10 +226,78 @@ public function dataProviderWithFilter()
{
return array(
array('--suffixes', '.class.php'),
- array('--exclude', 'ccn_,npath_'),
+ array('--exclude', 'ccn_,npath_,parse_error'),
);
}
+ public function testMainGenerateBaseline()
+ {
+ $uri = str_replace("\\", "/", realpath(self::createFileUri('source/source_with_anonymous_class.php')));
+ $temp = self::createTempFileUri();
+ $exitCode = Command::main(array(
+ __FILE__,
+ $uri,
+ 'text',
+ 'naming',
+ '--generate-baseline',
+ '--baseline-file',
+ $temp,
+ ));
+
+ static::assertSame(Command::EXIT_SUCCESS, $exitCode);
+ static::assertFileExists($temp);
+ static::assertContains($uri, file_get_contents($temp));
+ }
+
+ /**
+ * Testcase:
+ * - Class has existing ShortVariable and new BooleanGetMethodName violations
+ * - Baseline has ShortVariable and LongClassName baseline violations
+ * Expect in baseline:
+ * - LongClassName violation should be removed
+ * - ShortVariable violation should still exist
+ * - BooleanGetMethodName shouldn't be added
+ */
+ public function testMainUpdateBaseline()
+ {
+ $sourceTemp = self::createTempFileUri('ClassWithMultipleViolations.php');
+ $baselineTemp = self::createTempFileUri();
+ copy(static::createResourceUriForTest('UpdateBaseline/ClassWithMultipleViolations.php'), $sourceTemp);
+ copy(static::createResourceUriForTest('UpdateBaseline/phpmd.baseline.xml'), $baselineTemp);
+
+ $exitCode = Command::main(array(
+ __FILE__,
+ $sourceTemp,
+ 'text',
+ 'naming',
+ '--update-baseline',
+ '--baseline-file',
+ $baselineTemp,
+ ));
+
+ static::assertSame(Command::EXIT_SUCCESS, $exitCode);
+ static::assertXmlStringEqualsXmlString(
+ file_get_contents(static::createResourceUriForTest('UpdateBaseline/expected.baseline.xml')),
+ file_get_contents($baselineTemp)
+ );
+ }
+
+ public function testMainBaselineViolationShouldBeIgnored()
+ {
+ $sourceFile = realpath(static::createResourceUriForTest('Baseline/ClassWithShortVariable.php'));
+ $baselineFile = realpath(static::createResourceUriForTest('Baseline/phpmd.baseline.xml'));
+ $exitCode = Command::main(array(
+ __FILE__,
+ $sourceFile,
+ 'text',
+ 'naming',
+ '--baseline-file',
+ $baselineFile,
+ ));
+
+ static::assertSame(Command::EXIT_SUCCESS, $exitCode);
+ }
+
public function testMainWritesExceptionMessageToStderr()
{
stream_filter_register('stderr_stream', 'PHPMD\\TextUI\\StreamFilter');
@@ -220,7 +332,7 @@ public function testMainPrintsVersionToStdout()
)
);
- $data = @parse_ini_file(__DIR__ . '/../../../../../build.properties');
+ $data = @parse_ini_file(__DIR__ . '/../../../../../build.properties');
$version = $data['project.version'];
$this->assertEquals('PHPMD ' . $version, trim(StreamFilter::$streamHandle));
diff --git a/src/test/php/PHPMD/Utility/PathsTest.php b/src/test/php/PHPMD/Utility/PathsTest.php
new file mode 100644
index 000000000..b7a634c40
--- /dev/null
+++ b/src/test/php/PHPMD/Utility/PathsTest.php
@@ -0,0 +1,96 @@
+getStream());
+ }
+}
diff --git a/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.baseline.xml b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.baseline.xml
new file mode 100644
index 000000000..5a933716b
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.baseline.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.xml b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.xml
new file mode 100644
index 000000000..c6b22a7b0
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineFileFinder/testA/phpmd.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineFileFinder/testB/phpmd.xml b/src/test/resources/files/Baseline/BaselineFileFinder/testB/phpmd.xml
new file mode 100644
index 000000000..c6b22a7b0
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineFileFinder/testB/phpmd.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/baseline.xml
new file mode 100644
index 000000000..7c19c20da
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/baseline.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/invalid-baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/invalid-baseline.xml
new file mode 100644
index 000000000..663e2aee6
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/invalid-baseline.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/missing-file-baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/missing-file-baseline.xml
new file mode 100644
index 000000000..b4cd528e2
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/missing-file-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/test/resources/files/Baseline/BaselineSetFactory/missing-rule-baseline.xml b/src/test/resources/files/Baseline/BaselineSetFactory/missing-rule-baseline.xml
new file mode 100644
index 000000000..0c54c3d25
--- /dev/null
+++ b/src/test/resources/files/Baseline/BaselineSetFactory/missing-rule-baseline.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToDynamicClassName.php b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToDynamicClassName.php
new file mode 100644
index 000000000..ace0d86e0
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/MissingImport/testRuleDoesNotApplyToDynamicClassName.php
@@ -0,0 +1,28 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+namespace PHPMDTest;
+
+class testRuleDoesNotApplyToDynamicClassName
+{
+ public function testRuleDoesNotApplyToDynamicClassName()
+ {
+ $className = 'DateTime';
+
+ return (new $className)->format('Y');
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToExtraParameters.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToExtraParameters.php
new file mode 100644
index 000000000..d4d63e28d
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToExtraParameters.php
@@ -0,0 +1,31 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToExtraParameters
+{
+ function testRuleAppliesToExtraParameters()
+ {
+ $x = 42;
+
+ $this->foo($x, $y);
+ }
+
+ function foo(&$a)
+ {
+ $a++;
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToMethodMatchingFunctionName.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToMethodMatchingFunctionName.php
new file mode 100644
index 000000000..b8c8046ef
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToMethodMatchingFunctionName.php
@@ -0,0 +1,29 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToMethodMatchingFunctionName
+{
+ public function testRuleAppliesToMethodMatchingFunctionName()
+ {
+ $this->preg_match('a', 'b', $undefined);
+ }
+
+ public function preg_match($a, $b, $c)
+ {
+ // noop
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php
index 0937f52a7..5f9ba6ef7 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArray.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToUndefinedVariableOnArray extends AbstractTest
+class testRuleAppliesToUndefinedVariableOnArray
{
function testRuleAppliesToUndefinedVariableOnArray()
{
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php
index cdba11288..12dfca0f8 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableOnArrayWithKeys.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToUndefinedVariableOnArray extends AbstractTest
+class testRuleAppliesToUndefinedVariableOnArray
{
function testRuleAppliesToUndefinedVariableOnArrayWithKeys()
{
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php
index 39f2d95d4..9f088e737 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUndefinedVariableWithDefinedVariable.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToUndefinedVariableWithDefinedVariable extends AbstractTest
+class testRuleAppliesToUndefinedVariableWithDefinedVariable
{
function testRuleAppliesToUndefinedVariableWithDefinedVariable()
{
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUnknownArguments.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUnknownArguments.php
new file mode 100644
index 000000000..4850530b2
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleAppliesToUnknownArguments.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToUnknownArguments
+{
+ function testRuleAppliesToUnknownArguments(UnknownObject $object)
+ {
+ $object->foo($a);
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUnknownMethod.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUnknownMethod.php
new file mode 100644
index 000000000..e31411476
--- /dev/null
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUnknownMethod.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToUnknownMethod
+{
+ function testRuleDoesNotApplyToUnknownMethod(UnknownObject $object)
+ {
+ $this->foo($object);
+ }
+}
diff --git a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php
index 81598cf6a..528f8501d 100644
--- a/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php
+++ b/src/test/resources/files/Rule/CleanCode/UndefinedVariable/testRuleDoesNotApplyToUsedProperties.php
@@ -15,7 +15,7 @@
* @link http://phpmd.org/
*/
-class testRuleDoesNotApplyToUsedProperties extends AbstractTest
+class testRuleDoesNotApplyToUsedProperties
{
protected $x = 'abc';
diff --git a/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed.php b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed.php
new file mode 100644
index 000000000..588302650
--- /dev/null
+++ b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed.php
@@ -0,0 +1,24 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleAppliesToTestMethodWithTwoConsecutiveUnderscoresWhenAllowed
+{
+ public function testGivenSomeValue_expectSome__niceResult()
+ {
+
+ }
+}
diff --git a/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed.php b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed.php
similarity index 79%
rename from src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed.php
rename to src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed.php
index 751d73ce8..d506183de 100644
--- a/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed.php
+++ b/src/test/resources/files/Rule/Controversial/CamelCaseMethodName/testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed.php
@@ -15,9 +15,9 @@
* @link http://phpmd.org/
*/
-class testRuleAppliesToTestMethodWithTwoUnderscoresEvenWhenOneIsAllowed
+class testRuleDoesNotApplyForTestMethodWithMultipleUnderscoresWhenAllowed
{
- public function testGivenSomeValue_expectSome_niceResult()
+ public function testGivenSomeValue_expect_some_result()
{
}
diff --git a/src/test/resources/files/Rule/Design/TooManyPublicMethods/testRuleApplyToBasicClass.php b/src/test/resources/files/Rule/Design/TooManyPublicMethods/testRuleApplyToBasicClass.php
new file mode 100644
index 000000000..c25a71677
--- /dev/null
+++ b/src/test/resources/files/Rule/Design/TooManyPublicMethods/testRuleApplyToBasicClass.php
@@ -0,0 +1,36 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+function testRuleNotAppliesToTryCatchBlocksInsideForeach()
+{
+ foreach ([1, 2] as $number) {
+ try {
+ echo $number;
+ } catch (InvalidArgumentException $e) {
+ var_dump($e);
+ }
+ }
+}
diff --git a/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToCompactWithDoubleQuotesFunction.php b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToCompactWithDoubleQuotesFunction.php
new file mode 100755
index 000000000..f946564a2
--- /dev/null
+++ b/src/test/resources/files/Rule/UnusedLocalVariable/testRuleDoesNotApplyToCompactWithDoubleQuotesFunction.php
@@ -0,0 +1,26 @@
+.
+ * All rights reserved.
+ *
+ * Licensed under BSD License
+ * For full copyright and license information, please see the LICENSE file.
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @author Manuel Pichler
+ * @copyright Manuel Pichler. All rights reserved.
+ * @license https://opensource.org/licenses/bsd-license.php BSD License
+ * @link http://phpmd.org/
+ */
+
+class testRuleDoesNotApplyToCompactWithDoubleQuotesFunction
+{
+ public function testRuleDoesNotApplyToCompactWithDoubleQuotesFunction()
+ {
+ $key = 'ok';
+
+ return compact("key");
+ }
+}
diff --git a/src/test/resources/files/TextUI/Command/Baseline/ClassWithShortVariable.php b/src/test/resources/files/TextUI/Command/Baseline/ClassWithShortVariable.php
new file mode 100644
index 000000000..7b1adbcd6
--- /dev/null
+++ b/src/test/resources/files/TextUI/Command/Baseline/ClassWithShortVariable.php
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/src/test/resources/files/TextUI/Command/UpdateBaseline/ClassWithMultipleViolations.php b/src/test/resources/files/TextUI/Command/UpdateBaseline/ClassWithMultipleViolations.php
new file mode 100644
index 000000000..2f89ee6f4
--- /dev/null
+++ b/src/test/resources/files/TextUI/Command/UpdateBaseline/ClassWithMultipleViolations.php
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/src/test/resources/files/TextUI/Command/UpdateBaseline/phpmd.baseline.xml b/src/test/resources/files/TextUI/Command/UpdateBaseline/phpmd.baseline.xml
new file mode 100644
index 000000000..4385c988f
--- /dev/null
+++ b/src/test/resources/files/TextUI/Command/UpdateBaseline/phpmd.baseline.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/test/resources/files/Utility/Paths/resource.txt b/src/test/resources/files/Utility/Paths/resource.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/test/resources/files/pmd/single-directory.xml b/src/test/resources/files/pmd/single-directory.xml
index 4259d677a..e5656efdf 100755
--- a/src/test/resources/files/pmd/single-directory.xml
+++ b/src/test/resources/files/pmd/single-directory.xml
@@ -18,4 +18,5 @@
The method doSomething() has an NPath complexity of 50000. The configured NPath complexity threshold is 50.
+
\ No newline at end of file
diff --git a/src/test/resources/files/renderer/baseline_renderer_expected1.xml b/src/test/resources/files/renderer/baseline_renderer_expected1.xml
new file mode 100644
index 000000000..b1a7f932e
--- /dev/null
+++ b/src/test/resources/files/renderer/baseline_renderer_expected1.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/test/resources/files/renderer/baseline_renderer_expected2.xml b/src/test/resources/files/renderer/baseline_renderer_expected2.xml
new file mode 100644
index 000000000..001b78a22
--- /dev/null
+++ b/src/test/resources/files/renderer/baseline_renderer_expected2.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/test/resources/files/renderer/baseline_renderer_expected3.xml b/src/test/resources/files/renderer/baseline_renderer_expected3.xml
new file mode 100644
index 000000000..5a933716b
--- /dev/null
+++ b/src/test/resources/files/renderer/baseline_renderer_expected3.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/test/resources/files/renderer/sarif_renderer_expected.sarif b/src/test/resources/files/renderer/sarif_renderer_expected.sarif
new file mode 100644
index 000000000..41cea6ca7
--- /dev/null
+++ b/src/test/resources/files/renderer/sarif_renderer_expected.sarif
@@ -0,0 +1,137 @@
+{
+ "version": "2.1.0",
+ "$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "PHPMD",
+ "informationUri": "https:\/\/phpmd.org",
+ "version": "@package_version@",
+ "rules": [
+ {
+ "id": "TestRuleSet\/RuleStub",
+ "name": "RuleStub",
+ "shortDescription": {
+ "text": "TestRuleSet: RuleStub"
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Test description"
+ }
+ },
+ "help": {
+ "text": "Simple rule stub"
+ },
+ "helpUri": "https:\/\/phpmd.org\/rules\/index.html",
+ "properties": {
+ "ruleSet": "TestRuleSet",
+ "priority": 5,
+ "since": "PHPMD 42.23"
+ }
+ },
+ {
+ "id": "TestRuleSet\/AnotherRuleStub",
+ "name": "AnotherRuleStub",
+ "shortDescription": {
+ "text": "TestRuleSet: AnotherRuleStub"
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Test description"
+ }
+ },
+ "help": {
+ "text": "Simple rule stub",
+ "markdown": "Simple rule stub\n\n### Example\n\n```php\nclass Example\n{\n}\n```\n\n```php\nclass AnotherExample\n{\n public $var;\n}\n```"
+ },
+ "helpUri": "https:\/\/phpmd.org\/rules\/index.html",
+ "properties": {
+ "ruleSet": "TestRuleSet",
+ "priority": 5
+ }
+ }
+ ]
+ }
+ },
+ "originalUriBaseIds": {
+ "WORKINGDIR": {
+ "uri": "file:\/\/#{workingDirectory}\/"
+ }
+ },
+ "results": [
+ {
+ "ruleId": "TestRuleSet\/RuleStub",
+ "ruleIndex": 0,
+ "message": {
+ "id": "default",
+ "arguments": [],
+ "text": "Test description"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/bar.php"
+ },
+ "region": {
+ "startLine": 23,
+ "endLine": 42
+ }
+ }
+ }
+ ]
+ },
+ {
+ "ruleId": "TestRuleSet\/RuleStub",
+ "ruleIndex": 0,
+ "message": {
+ "id": "default",
+ "arguments": [],
+ "text": "Test description"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/foo.php"
+ },
+ "region": {
+ "startLine": 23,
+ "endLine": 42
+ }
+ }
+ }
+ ]
+ },
+ {
+ "ruleId": "TestRuleSet\/AnotherRuleStub",
+ "ruleIndex": 1,
+ "message": {
+ "id": "default",
+ "arguments": [
+ "123",
+ "3.2",
+ "awesomeFunction()"
+ ],
+ "text": "Test description"
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "src\/foobar.php",
+ "uriBaseId": "WORKINGDIR"
+ },
+ "region": {
+ "startLine": 23,
+ "endLine": 42
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/test/resources/files/renderer/sarif_renderer_processing_errors.sarif b/src/test/resources/files/renderer/sarif_renderer_processing_errors.sarif
new file mode 100644
index 000000000..d8d273cac
--- /dev/null
+++ b/src/test/resources/files/renderer/sarif_renderer_processing_errors.sarif
@@ -0,0 +1,84 @@
+{
+ "version": "2.1.0",
+ "$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "PHPMD",
+ "informationUri": "https:\/\/phpmd.org",
+ "version": "@package_version@",
+ "rules": []
+ }
+ },
+ "originalUriBaseIds": {
+ "WORKINGDIR": {
+ "uri": "file:\/\/#{workingDirectory}\/"
+ }
+ },
+ "results": [
+ {
+ "level": "error",
+ "message": {
+ "text": "Failed for file \u0022\/tmp\/foo.php\u0022."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/tmp\/foo.php"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "level": "error",
+ "message": {
+ "text": "Failed for file \u0022\/tmp\/bar.php\u0022."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/tmp\/bar.php"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "level": "error",
+ "message": {
+ "text": "Failed for file \u0022#{rootDirectory}\/foobar.php\u0022."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "src\/test\/resources\/files\/foobar.php",
+ "uriBaseId": "WORKINGDIR"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "level": "error",
+ "message": {
+ "text": "Cannot read file \u0022\/tmp\/foo.php\u0022. Permission denied."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "file:\/\/\/tmp\/foo.php"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/test/resources/files/source/source_with_parse_error.php b/src/test/resources/files/source/source_with_parse_error.php
new file mode 100644
index 000000000..5171c3c95
--- /dev/null
+++ b/src/test/resources/files/source/source_with_parse_error.php
@@ -0,0 +1,3 @@
+