diff --git a/src/Baseline/BaselineSet.php b/src/Baseline/BaselineSet.php new file mode 100644 index 0000000000..7370f9bdae --- /dev/null +++ b/src/Baseline/BaselineSet.php @@ -0,0 +1,66 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Baseline; + +class BaselineSet +{ + + /** + * A collection of a baselined violations + * + * @var array + */ + private $violations = []; + + + /** + * Add a single entry to the baseline set + * + * @param ViolationBaseline $entry the entry to add to the collection + * + * @return void + */ + public function addEntry(ViolationBaseline $entry) + { + $this->violations[$entry->getSniffName()][$entry->getSignature()][] = $entry; + + }//end addEntry() + + + /** + * Test if the given sniff and filename is in the baseline collection + * + * @param string $sniffName the name of the sniff to search for + * @param string $fileName the full filename of the file to match + * @param string $signature the code signature of the violation + * + * @return bool + */ + public function contains($sniffName, $fileName, $signature) + { + if (isset($this->violations[$sniffName][$signature]) === false) { + return false; + } + + // Normalize slashes in file name. + $fileName = str_replace('\\', '/', $fileName); + + foreach ($this->violations[$sniffName][$signature] as $baseline) { + if ($baseline->matches($fileName) === true) { + return true; + } + } + + return false; + + }//end contains() + + +}//end class diff --git a/src/Baseline/BaselineSetFactory.php b/src/Baseline/BaselineSetFactory.php new file mode 100644 index 0000000000..cbe45195cb --- /dev/null +++ b/src/Baseline/BaselineSetFactory.php @@ -0,0 +1,67 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Baseline; + +use PHP_CodeSniffer\Exceptions\RuntimeException; + +class BaselineSetFactory +{ + + + /** + * Read the baseline violations from the given filename path. + * + * @param string $fileName the baseline file to import + * + * @return BaselineSet|null + * @throws RuntimeException + */ + public static function fromFile($fileName) + { + if (file_exists($fileName) === false) { + return null; + } + + $xml = @simplexml_load_string(file_get_contents($fileName)); + if ($xml === false) { + throw new RuntimeException('Unable to read xml from: '.$fileName); + } + + $baselineSet = new BaselineSet(); + + foreach ($xml->children() as $node) { + if ($node->getName() !== 'violation') { + continue; + } + + if (isset($node['sniff']) === false) { + throw new RuntimeException('Missing `sniff` attribute in `violation` in '.$fileName); + } + + if (isset($node['file']) === false) { + throw new RuntimeException('Missing `file` attribute in `violation` in '.$fileName); + } + + if (isset($node['signature']) === false) { + throw new RuntimeException('Missing `signature` attribute in `violation` in '.$fileName); + } + + // Normalize filepath (if needed). + $filePath = '/'.ltrim(str_replace('\\', '/', (string) $node['file']), '/'); + + $baselineSet->addEntry(new ViolationBaseline((string) $node['sniff'], $filePath, (string) $node['signature'])); + }//end foreach + + return $baselineSet; + + }//end fromFile() + + +}//end class diff --git a/src/Baseline/ViolationBaseline.php b/src/Baseline/ViolationBaseline.php new file mode 100644 index 0000000000..ecba0101ea --- /dev/null +++ b/src/Baseline/ViolationBaseline.php @@ -0,0 +1,99 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Baseline; + +class ViolationBaseline +{ + + /** + * The name of the sniff + * + * @var string + */ + private $sniffName; + + /** + * The relative file path + * + * @var string + */ + private $fileName; + + /** + * The length of the filename to improve comparison performance + * + * @var integer + */ + private $fileNameLength; + + /** + * The code signature for the baseline + * + * @var string + */ + private $signature; + + + /** + * Initialize the violation baseline + * + * @param string $sniffName The name of the sniff that's baselined. + * @param string $fileName The relative file path. + * @param string $signature The code signature for the baseline. + */ + public function __construct($sniffName, $fileName, $signature) + { + $this->sniffName = $sniffName; + $this->fileName = $fileName; + $this->fileNameLength = strlen($fileName); + $this->signature = $signature; + + }//end __construct() + + + /** + * Get the sniff name that was baselined + * + * @return string + */ + public function getSniffName() + { + return $this->sniffName; + + }//end getSniffName() + + + /** + * Get the code signature for this baseline + * + * @return string + */ + public function getSignature() + { + return $this->signature; + + }//end getSignature() + + + /** + * Test if the given filepath matches the relative filename in the baseline + * + * @param string $filepath the full filepath to match against + * + * @return bool + */ + public function matches($filepath) + { + return substr($filepath, -$this->fileNameLength) === $this->fileName; + + }//end matches() + + +}//end class diff --git a/src/Config.php b/src/Config.php index 0320bdee87..92224e521e 100644 --- a/src/Config.php +++ b/src/Config.php @@ -12,6 +12,7 @@ namespace PHP_CodeSniffer; +use PHP_CodeSniffer\Baseline\BaselineSetFactory; use PHP_CodeSniffer\Exceptions\DeepExitException; use PHP_CodeSniffer\Exceptions\RuntimeException; use PHP_CodeSniffer\Util\Common; @@ -45,6 +46,7 @@ * If empty, all sniffs in the supplied standards will be used. * @property string[] $ignored Regular expressions used to ignore files and folders during checking. * @property string $reportFile A file where the report output should be written. + * @property string $baselineFile The baselined violations file to include. * @property string $generator The documentation generator to use. * @property string $filter The filter to use for the run. * @property string[] $bootstrap One of more files to include before the run begins. @@ -130,6 +132,7 @@ class Config 'exclude' => null, 'ignored' => null, 'reportFile' => null, + 'baselineFile' => null, 'generator' => null, 'filter' => null, 'bootstrap' => null, @@ -146,6 +149,13 @@ class Config 'unknown' => null, ]; + /** + * The configured baselined violations + * + * @var \PHP_CodeSniffer\Baseline\BaselineSet|null + */ + public $baseline = null; + /** * Whether or not to kill the process when an unknown command line arg is found. * @@ -381,6 +391,11 @@ public function __construct(array $cliArgs=[], $dieOnUnknownArg=true) } while ($currentDir !== '.' && $currentDir !== $lastDir && Common::isReadable($currentDir) === true); }//end if + // Load baseline file, only if no baseline should be created. + if (isset($this->settings['reports']['baseline']) === false) { + $this->baseline = BaselineSetFactory::fromFile($this->baselineFile); + } + if (defined('STDIN') === false || stripos(PHP_OS, 'WIN') === 0 ) { @@ -504,6 +519,7 @@ public function restoreDefaults() $this->exclude = []; $this->ignored = []; $this->reportFile = null; + $this->baselineFile = 'phpcs.baseline.xml'; $this->generator = null; $this->filter = null; $this->bootstrap = []; @@ -1038,6 +1054,18 @@ public function processLongArgument($arg, $pos) $error .= $this->printShortUsage(true); throw new DeepExitException($error, 3); } + } else if (substr($arg, 0, 13) === 'baselineFile=') { + if (substr($arg, 13) === '') { + $this->basepath = null; + break; + } + + $this->baselineFile = Util\Common::realpath(substr($arg, 13)); + if (is_file($this->baselineFile) === false) { + $error = 'ERROR: The specified baselineFile "'.substr($arg, 13).'" points to a non-existent file'.PHP_EOL.PHP_EOL; + $error .= $this->printShortUsage(true); + throw new DeepExitException($error, 3); + } } else if ((substr($arg, 0, 7) === 'report=' || substr($arg, 0, 7) === 'report-')) { $reports = []; @@ -1361,9 +1389,10 @@ public function printPHPCSUsage() echo 'Usage: phpcs [-nwlsaepqvi] [-d key[=value]] [--colors] [--no-colors]'.PHP_EOL; echo ' [--cache[=]] [--no-cache] [--tab-width=]'.PHP_EOL; echo ' [--report=] [--report-file=] [--report-=]'.PHP_EOL; - echo ' [--report-width=] [--basepath=] [--bootstrap=]'.PHP_EOL; - echo ' [--severity=] [--error-severity=] [--warning-severity=]'.PHP_EOL; - echo ' [--runtime-set key value] [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL; + echo ' [--report-width=] [--basepath=] [--baselineFile=]'.PHP_EOL; + echo ' [--bootstrap=] [--severity=] [--error-severity=]'.PHP_EOL; + echo ' [--warning-severity=] [--runtime-set key value]'.PHP_EOL; + echo ' [--config-set key value] [--config-delete key] [--config-show]'.PHP_EOL; echo ' [--standard=] [--sniffs=] [--exclude=]'.PHP_EOL; echo ' [--encoding=] [--parallel=] [--generator=]'.PHP_EOL; echo ' [--extensions=] [--ignore=] [--ignore-annotations]'.PHP_EOL; @@ -1396,6 +1425,7 @@ public function printPHPCSUsage() echo PHP_EOL; echo ' Use a specific file for caching (uses a temporary file by default)'.PHP_EOL; echo ' A path to strip from the front of file paths inside reports'.PHP_EOL; + echo ' The path to the file with the baselined violations'.PHP_EOL; echo ' A comma separated list of files to run before processing begins'.PHP_EOL; echo ' The encoding of the files being checked (default is utf-8)'.PHP_EOL; echo ' A comma separated list of file extensions to check'.PHP_EOL; diff --git a/src/Files/File.php b/src/Files/File.php index 21cbeb2158..39f170c55f 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -1037,6 +1037,14 @@ protected function addMessage($error, $message, $line, $column, $code, $data, $s return false; } + // The message is part of the baselined violations. + if ($this->config->baseline !== null) { + $signature = Util\CodeSignature::createSignature($this->getTokens(), $line); + if ($this->config->baseline->contains($sniffCode, $this->path, $signature) === true) { + return false; + } + } + $messageCount++; if ($fixable === true) { $this->fixableCount++; diff --git a/src/Reports/Baseline.php b/src/Reports/Baseline.php new file mode 100644 index 0000000000..e5e965fe78 --- /dev/null +++ b/src/Reports/Baseline.php @@ -0,0 +1,120 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Reports; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Util; +use XMLWriter; + +class Baseline implements Report +{ + + + /** + * Generate a partial report for a single processed file. + * Function should return TRUE if it printed or stored data about the file + * and FALSE if it ignored the file. Returning TRUE indicates that the file and + * its data should be counted in the grand totals. + * + * @param array $report Prepared report data. + * @param File $phpcsFile The file being reported on. + * @param bool $showSources Show sources? + * @param int $width Maximum allowed line width. + * + * @return bool + */ + public function generateFileReport($report, File $phpcsFile, $showSources=false, $width=80) + { + $out = new XMLWriter; + $out->openMemory(); + $out->setIndent(true); + $out->setIndentString(' '); + $out->startDocument('1.0', 'UTF-8'); + + if ($report['errors'] === 0 && $report['warnings'] === 0) { + // Nothing to print. + return false; + } + + foreach ($report['messages'] as $lineNr => $lineErrors) { + $signature = Util\CodeSignature::createSignature($phpcsFile->getTokens(), $lineNr); + + foreach ($lineErrors as $colErrors) { + foreach ($colErrors as $error) { + $out->startElement('violation'); + $out->writeAttribute('file', $report['filename']); + $out->writeAttribute('sniff', $error['source']); + $out->writeAttribute('signature', $signature); + + $out->endElement(); + } + } + } + + // Remove the start of the document because we will + // add that manually later. We only have it in here to + // properly set the encoding. + $content = $out->flush(); + $content = preg_replace("/[\n\r]/", PHP_EOL, $content); + $content = substr($content, (strpos($content, PHP_EOL) + strlen(PHP_EOL))); + + echo $content; + + return true; + + }//end generateFileReport() + + + /** + * Prints all violations for processed files, in a proprietary XML format. + * + * @param string $cachedData Any partial report data that was returned from + * generateFileReport during the run. + * @param int $totalFiles Total number of files processed during the run. + * @param int $totalErrors Total number of errors found during the run. + * @param int $totalWarnings Total number of warnings found during the run. + * @param int $totalFixable Total number of problems that can be fixed. + * @param bool $showSources Show sources? + * @param int $width Maximum allowed line width. + * @param bool $interactive Are we running in interactive mode? + * @param bool $toScreen Is the report being printed to screen? + * + * @return void + */ + public function generate( + $cachedData, + $totalFiles, + $totalErrors, + $totalWarnings, + $totalFixable, + $showSources=false, + $width=80, + $interactive=false, + $toScreen=true + ) { + echo ''.PHP_EOL; + echo ''; + + // Split violations on line-ending, make them unique and sort them. + if ($cachedData !== "") { + $lines = explode(PHP_EOL, $cachedData); + $lines = array_unique($lines); + sort($lines); + $cachedData = implode(PHP_EOL, $lines); + } + + echo $cachedData; + echo PHP_EOL.''.PHP_EOL; + + }//end generate() + + +}//end class diff --git a/src/Util/CodeSignature.php b/src/Util/CodeSignature.php new file mode 100644 index 0000000000..5c3cf7cc4d --- /dev/null +++ b/src/Util/CodeSignature.php @@ -0,0 +1,48 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Util; + +class CodeSignature +{ + + + /** + * Generate the sha1 code signature for the tokens around the given line. + * + * @param array $tokens All tokens of a given file. + * @param int $lineNr The lineNr to search for tokens. + * + * @return string The sha1 hash of the tokens around the given line + */ + public static function createSignature(array $tokens, $lineNr) + { + // Get all tokens one line before and after. + $start = ($lineNr - 1); + $end = ($lineNr + 1); + + $content = ''; + foreach ($tokens as $token) { + if ($token['line'] > $end) { + break; + } + + // Concat content excluding line endings. + if ($token['line'] >= $start && isset($token['content']) === true) { + $content .= trim($token['content'], "\r\n"); + } + } + + // Generate sha1 hash. + return hash('sha1', $content); + + }//end createSignature() + + +}//end class diff --git a/tests/Core/Baseline/BaselineSetFactoryTest.php b/tests/Core/Baseline/BaselineSetFactoryTest.php new file mode 100644 index 0000000000..3de5487a3a --- /dev/null +++ b/tests/Core/Baseline/BaselineSetFactoryTest.php @@ -0,0 +1,129 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Baseline; + +use PHP_CodeSniffer\Baseline\BaselineSetFactory; +use PHPUnit\Framework\TestCase; + +/** + * Testcases for the reading the baseline set from file + * + * @coversDefaultClass \PHP_CodeSniffer\Baseline\BaselineSetFactory + */ +class BaselineSetFactoryTest extends TestCase +{ + + + /** + * Read the baseline from a file + * + * @covers ::fromFile + * @return void + */ + public function testFromFileShouldSucceed() + { + $filename = __DIR__.'/TestFiles/baseline.xml'; + $set = BaselineSetFactory::fromFile($filename); + + static::assertTrue($set->contains('Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterOpen', '/test/src/foo/bar', 'foobar')); + + }//end testFromFileShouldSucceed() + + + /** + * Read the baseline from a file with different slashes + * + * @covers ::fromFile + * @return void + */ + public function testFromFileShouldSucceedWithBackAndForwardSlashes() + { + $filename = __DIR__.'/TestFiles/baseline.xml'; + $set = BaselineSetFactory::fromFile($filename); + + static::assertTrue($set->contains('Squiz.Functions.FunctionDeclarationArgumentSpacing.SpacingAfterOpen', '/test\\src\\foo/bar', 'foobar')); + + }//end testFromFileShouldSucceedWithBackAndForwardSlashes() + + + /** + * Test that reading absent file returns null + * + * @covers ::fromFile + * @return void + */ + public function testFromFileShouldReturnNullIfAbsent() + { + static::assertNull(BaselineSetFactory::fromFile('foobar.xml')); + + }//end testFromFileShouldReturnNullIfAbsent() + + + /** + * Test that reading invalid xml throws exception + * + * @covers ::fromFile + * @return void + */ + public function testFromFileShouldThrowExceptionForOnInvalidXML() + { + $this->expectException('PHP_CodeSniffer\Exceptions\RuntimeException'); + $this->expectExceptionMessage('Unable to read xml from'); + BaselineSetFactory::fromFile(__DIR__.'/TestFiles/invalid-baseline.xml'); + + }//end testFromFileShouldThrowExceptionForOnInvalidXML() + + + /** + * Test that missing attributes throws exception + * + * @covers ::fromFile + * @return void + */ + public function testFromFileViolationMissingSniffShouldThrowException() + { + $this->expectException('PHP_CodeSniffer\Exceptions\RuntimeException'); + $this->expectExceptionMessage('Missing `sniff` attribute in `violation`'); + BaselineSetFactory::fromFile(__DIR__.'/TestFiles/missing-sniff-baseline.xml'); + + }//end testFromFileViolationMissingSniffShouldThrowException() + + + /** + * Test that missing signature attribute throws exception + * + * @covers ::fromFile + * @return void + */ + public function testFromFileViolationMissingSignatureShouldThrowException() + { + $this->expectException('PHP_CodeSniffer\Exceptions\RuntimeException'); + $this->expectExceptionMessage('Missing `signature` attribute in `violation` in'); + BaselineSetFactory::fromFile(__DIR__.'/TestFiles/missing-signature-baseline.xml'); + + }//end testFromFileViolationMissingSignatureShouldThrowException() + + + /** + * Test that missing attributes throws exception + * + * @covers ::fromFile + * @return void + */ + public function testFromFileViolationMissingFileShouldThrowException() + { + $this->expectException('PHP_CodeSniffer\Exceptions\RuntimeException'); + $this->expectExceptionMessage('Missing `file` attribute in `violation` in'); + BaselineSetFactory::fromFile(__DIR__.'/TestFiles/missing-file-baseline.xml'); + + }//end testFromFileViolationMissingFileShouldThrowException() + + +}//end class diff --git a/tests/Core/Baseline/BaselineSetTest.php b/tests/Core/Baseline/BaselineSetTest.php new file mode 100644 index 0000000000..588ee47925 --- /dev/null +++ b/tests/Core/Baseline/BaselineSetTest.php @@ -0,0 +1,80 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Baseline; + +use PHP_CodeSniffer\Baseline\BaselineSet; +use PHP_CodeSniffer\Baseline\ViolationBaseline; +use PHPUnit\Framework\TestCase; + +/** + * Test the logic of the baseline set + * + * @coversDefaultClass \PHP_CodeSniffer\Baseline\BaselineSet + */ +class BaselineSetTest extends TestCase +{ + + + /** + * Test that contains find the correct sniff + * + * @covers ::addEntry + * @covers ::contains + * @return void + */ + public function testSetContainsEntry() + { + $set = new BaselineSet(); + $set->addEntry(new ViolationBaseline('sniff', 'foobar', 'signature')); + + static::assertTrue($set->contains('sniff', 'foobar', 'signature')); + + }//end testSetContainsEntry() + + + /** + * Test that contains differentiates between types + * + * @covers ::addEntry + * @covers ::contains + * @return void + */ + public function testShouldFindEntryForIdenticalRules() + { + $set = new BaselineSet(); + $set->addEntry(new ViolationBaseline('sniff', 'foo', 'signA')); + $set->addEntry(new ViolationBaseline('sniff', 'bar', 'signB')); + + static::assertTrue($set->contains('sniff', 'foo', 'signA')); + static::assertTrue($set->contains('sniff', 'bar', 'signB')); + static::assertFalse($set->contains('sniff', 'unknown', 'signA')); + static::assertFalse($set->contains('sniff', 'foo', 'signB')); + + }//end testShouldFindEntryForIdenticalRules() + + + /** + * Test that unknown sniffs are not found + * + * @covers ::addEntry + * @covers ::contains + * @return void + */ + public function testShouldNotFindEntryForNonExistingRule() + { + $set = new BaselineSet(); + $set->addEntry(new ViolationBaseline('sniff', 'foo', 'signature')); + + static::assertFalse($set->contains('unknown', 'foo', 'signature')); + + }//end testShouldNotFindEntryForNonExistingRule() + + +}//end class diff --git a/tests/Core/Baseline/TestFiles/baseline.xml b/tests/Core/Baseline/TestFiles/baseline.xml new file mode 100644 index 0000000000..e9467b0291 --- /dev/null +++ b/tests/Core/Baseline/TestFiles/baseline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/Core/Baseline/TestFiles/invalid-baseline.xml b/tests/Core/Baseline/TestFiles/invalid-baseline.xml new file mode 100644 index 0000000000..54fb429d1d --- /dev/null +++ b/tests/Core/Baseline/TestFiles/invalid-baseline.xml @@ -0,0 +1,3 @@ + + + diff --git a/tests/Core/Baseline/TestFiles/missing-file-baseline.xml b/tests/Core/Baseline/TestFiles/missing-file-baseline.xml new file mode 100644 index 0000000000..51f024cebe --- /dev/null +++ b/tests/Core/Baseline/TestFiles/missing-file-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/Core/Baseline/TestFiles/missing-signature-baseline.xml b/tests/Core/Baseline/TestFiles/missing-signature-baseline.xml new file mode 100644 index 0000000000..af4d2c870b --- /dev/null +++ b/tests/Core/Baseline/TestFiles/missing-signature-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/Core/Baseline/TestFiles/missing-sniff-baseline.xml b/tests/Core/Baseline/TestFiles/missing-sniff-baseline.xml new file mode 100644 index 0000000000..bc50c10973 --- /dev/null +++ b/tests/Core/Baseline/TestFiles/missing-sniff-baseline.xml @@ -0,0 +1,4 @@ + + + + diff --git a/tests/Core/Baseline/ViolationBaselineTest.php b/tests/Core/Baseline/ViolationBaselineTest.php new file mode 100644 index 0000000000..14c256ba9a --- /dev/null +++ b/tests/Core/Baseline/ViolationBaselineTest.php @@ -0,0 +1,58 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Baseline; + +use PHP_CodeSniffer\Baseline\ViolationBaseline; +use PHPUnit\Framework\TestCase; + +/** + * Test the violation baseline data model + * + * @coversDefaultClass \PHP_CodeSniffer\Baseline\ViolationBaseline + */ +class ViolationBaselineTest extends TestCase +{ + + + /** + * Test the sniff name and signature is returned + * + * @covers ::__construct + * @covers ::getSniffName + * @covers ::getSignature + * @return void + */ + public function testAccessors() + { + $violation = new ViolationBaseline('sniff', 'foobar', 'signature'); + static::assertSame('sniff', $violation->getSniffName()); + static::assertSame('signature', $violation->getSignature()); + + }//end testAccessors() + + + /** + * Test the give file matches the baseline correctly + * + * @covers ::__construct + * @covers ::matches + * @return void + */ + public function testMatches() + { + $violation = new ViolationBaseline('sniff', 'foobar.txt', 'signature'); + static::assertTrue($violation->matches('foobar.txt')); + static::assertTrue($violation->matches('/test/foobar.txt')); + static::assertFalse($violation->matches('foo.txt')); + + }//end testMatches() + + +}//end class diff --git a/tests/Core/Reports/BaselineTest.php b/tests/Core/Reports/BaselineTest.php new file mode 100644 index 0000000000..e398593885 --- /dev/null +++ b/tests/Core/Reports/BaselineTest.php @@ -0,0 +1,116 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Reports; + +use PHP_CodeSniffer\Config; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Reports\Baseline; +use PHP_CodeSniffer\Util; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Testcases for the baseline report output file + * + * @coversDefaultClass \PHP_CodeSniffer\Reports\Baseline + */ +class BaselineTest extends TestCase +{ + + /** + * The mock file object + * + * @var File|MockObject + */ + private $file; + + + /** + * Setup the test mock object + * + * @return void + */ + protected function setup() + { + $this->file = $this->createMock('PHP_CodeSniffer\Files\File'); + + }//end setup() + + + /** + * Test that generation is skipped when there are no errors + * + * @covers ::generateFileReport + * @return void + */ + public function testGenerateFileReportEmptyShouldReturnFalse() + { + $report = new Baseline(); + static::assertFalse($report->generateFileReport(['errors' => 0, 'warnings' => 0], $this->file)); + + }//end testGenerateFileReportEmptyShouldReturnFalse() + + + /** + * Test the generation of a single error message + * + * @covers ::generateFileReport + * @return void + */ + public function testGenerateFileReportShouldPrintReport() + { + $reportData = [ + 'filename' => '/test/foobar.txt', + 'errors' => 1, + 'warnings' => 0, + 'messages' => [5 => [[['source' => 'MySniff']]]], + ]; + + $tokens = [ + [ + 'content' => 'foobar', + 'line' => 5, + ], + ]; + $signature = Util\CodeSignature::createSignature($tokens, 5); + $this->file->method('getTokens')->willReturn($tokens); + + $report = new Baseline(); + ob_start(); + static::assertTrue($report->generateFileReport($reportData, $this->file)); + $result = ob_get_clean(); + static::assertSame(''.PHP_EOL, $result); + + }//end testGenerateFileReportShouldPrintReport() + + + /** + * Test the generation of the complete file + * + * @covers ::generate + * @return void + */ + public function testGenerate() + { + $expected = "".PHP_EOL; + $expected .= ""; + $expected .= "".PHP_EOL; + $expected .= "".PHP_EOL; + + $report = new Baseline(); + ob_start(); + $report->generate('', 1, 1, 0, 1); + $result = ob_get_clean(); + static::assertSame($expected, $result); + + }//end testGenerate() + + +}//end class diff --git a/tests/Core/Util/CodeSignatureTest.php b/tests/Core/Util/CodeSignatureTest.php new file mode 100644 index 0000000000..8da0bb6a59 --- /dev/null +++ b/tests/Core/Util/CodeSignatureTest.php @@ -0,0 +1,95 @@ + + * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Util; + +use PHP_CodeSniffer\Util\CodeSignature; +use PHPUnit\Framework\TestCase; + +/** + * Tests the generation of code signature based on tokens + * + * @coversDefaultClass \PHP_CodeSniffer\Util\CodeSignature + */ +class CodeSignatureTest extends TestCase +{ + + + /** + * Test the code signature hash generation + * + * @param int $lineNr the line nr within the file + * @param string $expected the expected signature + * + * @return void + * + * @covers ::createSignature + * @dataProvider dataProvider + */ + public function testCreateSignature($lineNr, $expected) + { + $tokens = [ + [ + 'content' => 'line1', + 'line' => 1, + ], + [ + 'content' => 'line2', + 'line' => 2, + ], + [ + 'content' => 'line3', + 'line' => 3, + ], + [ + 'content' => "\r\n", + 'line' => 3, + ], + [ + 'content' => 'line4', + 'line' => 4, + ], + [ + 'content' => 'line5', + 'line' => 5, + ], + ]; + + $signature = CodeSignature::createSignature($tokens, $lineNr); + static::assertSame($expected, $signature); + + }//end testCreateSignature() + + + /** + * Provide edge case scenario's for the code signature + * + * @return array> + */ + public function dataProvider() + { + return [ + 'first line of file' => [ + 1, + hash('sha1', 'line1line2'), + ], + 'middle line of file' => [ + 3, + hash('sha1', 'line2line3line4'), + ], + 'last line of file' => [ + 5, + hash('sha1', 'line4line5'), + ], + ]; + + }//end dataProvider() + + +}//end class