From 3a742c0c37576684c8a257e2bd675469eabb2d34 Mon Sep 17 00:00:00 2001 From: Greg Sherwood Date: Fri, 25 Jul 2008 04:24:11 +0000 Subject: [PATCH] Added support for multi-file sniffs, which get a list of all sniffed files in a run for checking. Using these sniffs dramatically increases the memory requirements of the scirpt as all the token arrays are left around in memory for later use. There may be an alternative way of doing this, but I also don't want to slow the multi-file sniffs down too much by re-parsing everything; but I may have to. I've also added a sample multi-file sniff and its new unit tests (a specific strucutre is required for these) that checks if the same class name is used multiple times throughout your files. Could be useful if you check your whole project, but mostly just a sample. git-svn-id: http://svn.php.net/repository/pear/packages/PHP_CodeSniffer/trunk@263478 c90b9560-bf6c-de11-be94-00142212c4b1 --- CodeSniffer.php | 75 ++++- CodeSniffer/File.php | 27 +- CodeSniffer/MultiFileSniff.php | 48 ++++ .../Classes/DuplicateClassNameSniff.php | 74 +++++ .../Classes/DuplicateClassNameUnitTest.1.inc | 8 + .../Classes/DuplicateClassNameUnitTest.2.inc | 4 + .../Classes/DuplicateClassNameUnitTest.php | 82 ++++++ package.xml | 17 ++ tests/Standards/AbstractSniffUnitTest.php | 267 ++++++++++-------- 9 files changed, 474 insertions(+), 128 deletions(-) create mode 100755 CodeSniffer/MultiFileSniff.php create mode 100755 CodeSniffer/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php create mode 100755 CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.1.inc create mode 100755 CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.2.inc create mode 100755 CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.php diff --git a/CodeSniffer.php b/CodeSniffer.php index fe179c345a..9f2ec66ef6 100644 --- a/CodeSniffer.php +++ b/CodeSniffer.php @@ -33,6 +33,10 @@ throw new PHP_CodeSniffer_Exception('Interface PHP_CodeSniffer_Sniff not found'); } +if (interface_exists('PHP_CodeSniffer_MultiFileSniff', true) === false) { + throw new PHP_CodeSniffer_Exception('Interface PHP_CodeSniffer_MultiFileSniff not found'); +} + /** * PHP_CodeSniffer tokenises PHP code and detects violations of a * defined set of coding standards. @@ -89,6 +93,16 @@ class PHP_CodeSniffer */ protected $listeners = array(); + /** + * The listeners array, indexed by token type. + * + * @var array() + */ + private $_tokenListeners = array( + 'file' => array(), + 'multifile' => array(), + ); + /** * An array of patterns to use for skipping files. * @@ -267,8 +281,12 @@ public function process($files, $standard, array $sniffs=array(), $local=false) } // Reset the members. - $this->listeners = array(); - $this->files = array(); + $this->listeners = array(); + $this->files = array(); + $this->_tokenListeners = array( + 'file' => array(), + 'multifile' => array(), + ); if (PHP_CODESNIFFER_VERBOSITY > 0) { echo "Registering sniffs in $standard standard... "; @@ -283,6 +301,31 @@ public function process($files, $standard, array $sniffs=array(), $local=false) echo "DONE ($numSniffs sniffs registered)".PHP_EOL; } + // Construct a list of listeners indexed by token being listened for. + foreach ($this->listeners as $listenerClass) { + $listener = new $listenerClass(); + + if (($listener instanceof PHP_CodeSniffer_Sniff) === true) { + $tokens = $listener->register(); + if (is_array($tokens) === false) { + $msg = "Sniff $listenerClass register() method must return an array"; + throw new PHP_CodeSniffer_Exception($msg); + } + + foreach ($tokens as $token) { + if (isset($this->_tokenListeners['file'][$token]) === false) { + $this->_tokenListeners['file'][$token] = array(); + } + + if (in_array($listener, $this->_tokenListeners['file'][$token], true) === false) { + $this->_tokenListeners['file'][$token][] = $listener; + } + } + } else if (($listener instanceof PHP_CodeSniffer_MultiFileSniff) === true) { + $this->_tokenListeners['multifile'][] = $listener; + } + }//end foreach + foreach ($files as $file) { $this->file = $file; if (is_dir($this->file) === true) { @@ -292,6 +335,14 @@ public function process($files, $standard, array $sniffs=array(), $local=false) } } + // Now process the multi-file sniffs, assuming there are + // multiple files being sniffed. + if (count($files) > 1) { + foreach ($this->_tokenListeners['multifile'] as $listener) { + $listener->process($this->files); + } + } + }//end process() @@ -619,10 +670,16 @@ public function processFile($file, $contents=null) } } - $phpcsFile = new PHP_CodeSniffer_File($file, $this->listeners, $this->allowedFileExtensions); + $phpcsFile = new PHP_CodeSniffer_File($file, $this->_tokenListeners['file'], $this->allowedFileExtensions); $this->addFile($phpcsFile); $phpcsFile->start($contents); + // Clean up the test if we can to save memory. This can't be done if + // we need to leave the files around for multi-file sniffs. + if (empty($this->_tokenListeners['multifile']) === true) { + $phpcsFile->cleanUp(); + } + if (PHP_CODESNIFFER_VERBOSITY > 0) { $timeTaken = (time() - $startTime); if ($timeTaken === 0) { @@ -1099,6 +1156,18 @@ public function getSniffs() }//end getSniffs() + /** + * Gets the array of PHP_CodeSniffer_Sniff's indexed by token type. + * + * @return array() + */ + public function getTokenSniffs() + { + return $this->_tokenListeners; + + }//end getTokenSniffs() + + /** * Takes a token produced from token_get_all() and produces a * more uniform token. diff --git a/CodeSniffer/File.php b/CodeSniffer/File.php index 4b98e63c8b..a63e2d81bf 100644 --- a/CodeSniffer/File.php +++ b/CodeSniffer/File.php @@ -243,19 +243,8 @@ class PHP_CodeSniffer_File */ public function __construct($file, array $listeners, array $tokenizers) { - foreach ($listeners as $listenerClass) { - $listener = new $listenerClass(); - $tokens = $listener->register(); - - if (is_array($tokens) === false) { - $msg = "Sniff $listenerClass register() method must return an array"; - throw new PHP_CodeSniffer_Exception($msg); - } - - $this->addTokenListener($listener, $tokens); - } - $this->_file = trim($file); + $this->_listeners = $listeners; $this->tokenizers = $tokenizers; }//end __construct() @@ -391,12 +380,20 @@ public function start($contents=null) echo "\t*** END TOKEN PROCESSING ***".PHP_EOL; } - // We don't need the tokens any more, so get rid of them - // to save some memory. + }//end start() + + + /** + * Remove vars stored in this sniff that are no longer required. + * + * @return void + */ + public function cleanUp() + { $this->_tokens = null; $this->_listeners = null; - }//end start() + }//end cleanUp() /** diff --git a/CodeSniffer/MultiFileSniff.php b/CodeSniffer/MultiFileSniff.php new file mode 100755 index 0000000000..fa6e9fdd82 --- /dev/null +++ b/CodeSniffer/MultiFileSniff.php @@ -0,0 +1,48 @@ + + * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600) + * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence + * @version CVS: $Id$ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +/** + * Represents a PHP_CodeSniffer multi-file sniff for sniffing coding standards. + * + * A multi-file sniff is called after all files have been checked using the + * regular sniffs. The process() method is passed an array of PHP_CodeSniffer_File + * objects, one for each file checked during the script run. + * + * @category PHP + * @package PHP_CodeSniffer + * @author Greg Sherwood + * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600) + * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence + * @version Release: @package_version@ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ +interface PHP_CodeSniffer_MultiFileSniff +{ + + + /** + * Called once per script run to allow for processing of this sniff. + * + * @param array(PHP_CodeSniffer_File) $files The PHP_CodeSniffer files processed + * during the script run. + * + * @return void + */ + public function process(array $files); + + +}//end interface + +?> diff --git a/CodeSniffer/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php b/CodeSniffer/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php new file mode 100755 index 0000000000..4d56e0884d --- /dev/null +++ b/CodeSniffer/Standards/Generic/Sniffs/Classes/DuplicateClassNameSniff.php @@ -0,0 +1,74 @@ + + * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600) + * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence + * @version CVS: $Id$ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +/** + * Reports errors if the same class or interface name is used in multiple files. + * + * @category PHP + * @package PHP_CodeSniffer + * @author Greg Sherwood + * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600) + * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence + * @version Release: @package_version@ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ +class Generic_Sniffs_Classes_DuplicateClassNameSniff implements PHP_CodeSniffer_MultiFileSniff +{ + + + /** + * Called once per script run to allow for processing of this sniff. + * + * @param array(PHP_CodeSniffer_File) $files The PHP_CodeSniffer files processed + * during the script run. + * + * @return void + */ + public function process(array $files) + { + $foundClasses = array(); + + foreach ($files as $phpcsFile) { + $tokens = $phpcsFile->getTokens(); + + $stackPtr = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE), 0); + while ($stackPtr !== false) { + $nameToken = $phpcsFile->findNext(T_STRING, $stackPtr); + $name = $tokens[$nameToken]['content']; + $compareName = strtolower($name); + if (isset($foundClasses[$compareName]) === true) { + $type = strtolower($tokens[$stackPtr]['content']); + $file = $foundClasses[$compareName]['file']; + $line = $foundClasses[$compareName]['line']; + $error = "Duplicate $type name \"$name\" found; first defined in $file on line $line"; + $phpcsFile->addWarning($error, $stackPtr); + } else { + $foundClasses[$compareName] = array( + 'file' => $phpcsFile->getFilename(), + 'line' => $tokens[$stackPtr]['line'], + ); + } + + $stackPtr = $phpcsFile->findNext(array(T_CLASS, T_INTERFACE), ($stackPtr + 1)); + }//end while + + }//end foreach + + }//end process() + + +}//end class + +?> \ No newline at end of file diff --git a/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.1.inc b/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.1.inc new file mode 100755 index 0000000000..1b4be0b37a --- /dev/null +++ b/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.1.inc @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.2.inc b/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.2.inc new file mode 100755 index 0000000000..f5af896add --- /dev/null +++ b/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.2.inc @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.php b/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.php new file mode 100755 index 0000000000..ac988d6db4 --- /dev/null +++ b/CodeSniffer/Standards/Generic/Tests/Classes/DuplicateClassNameUnitTest.php @@ -0,0 +1,82 @@ + + * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600) + * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence + * @version CVS: $Id$ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ + +/** + * Unit test class for the DuplicateClassName multi-file sniff. + * + * A multi-file sniff unit test checks a .1.inc and a .2.inc file for expected violations + * of a single coding standard. Expected errors and warnings are stored in this class. + * + * @category PHP + * @package PHP_CodeSniffer + * @author Greg Sherwood + * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600) + * @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence + * @version Release: @package_version@ + * @link http://pear.php.net/package/PHP_CodeSniffer + */ +class Generic_Tests_Classes_DuplicateClassNameUnitTest extends AbstractSniffUnitTest +{ + + + /** + * Returns the lines where errors should occur. + * + * The key of the array should represent the line number and the value + * should represent the number of errors that should occur on that line. + * + * @return array(int => int) + */ + public function getErrorList() + { + return array(); + + }//end getErrorList() + + + /** + * Returns the lines where warnings should occur. + * + * The key of the array should represent the line number and the value + * should represent the number of warnings that should occur on that line. + * + * @return array(int => int) + */ + public function getWarningList($testFile='') + { + switch ($testFile) { + case 'DuplicateClassNameUnitTest.1.inc': + return array( + 6 => 1, + 7 => 1, + ); + break; + case 'DuplicateClassNameUnitTest.2.inc': + return array( + 2 => 1, + 3 => 1, + ); + break; + default: + return array(); + break; + }//end switch + + }//end getWarningList() + + +}//end class + +?> \ No newline at end of file diff --git a/package.xml b/package.xml index 773e584881..9879b91b1f 100644 --- a/package.xml +++ b/package.xml @@ -32,6 +32,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> BSD License + - Added support for a new multi-file sniff that sniffs all processed files at once + - Added Generic DuplicateClassNameSniff that will warn if the same class name is used in multiple files - Generic MultipleStatementAlignmentSniff now has correct error message if assignment is on a new line @@ -151,6 +153,11 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + @@ -249,6 +256,13 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + + + + + @@ -1335,6 +1349,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + + diff --git a/tests/Standards/AbstractSniffUnitTest.php b/tests/Standards/AbstractSniffUnitTest.php index 64ee95386b..ff3e3d67fa 100755 --- a/tests/Standards/AbstractSniffUnitTest.php +++ b/tests/Standards/AbstractSniffUnitTest.php @@ -115,9 +115,13 @@ protected final function runTest() $testFiles[] = $path; } } - }//end foreach + } + + // Get them in order. This is particularly important for multi-file sniffs. + sort($testFiles); $failureMessages = array(); + $multiFileSniff = false; foreach ($testFiles as $testFile) { try { self::$phpcs->process($testFile, $standardName, array($sniffClass)); @@ -125,55 +129,79 @@ protected final function runTest() $this->fail('An unexpected exception has been caught: '.$e->getMessage()); } + // After processing a file, check if the sniff was actually + // a multi-file sniff (i.e., had no indivdual file sniffs). + // If it is, we can skip checking of the other files and + // do a single multi-file check. + $sniffs = self::$phpcs->getTokenSniffs(); + if (empty($sniffs['file']) === true) { + $multiFileSniff = true; + break; + } + $files = self::$phpcs->getFiles(); $file = array_pop($files); - $foundErrors = $file->getErrors(); - $foundWarnings = $file->getWarnings(); - $expectedErrors = $this->getErrorList(basename($testFile)); - $expectedWarnings = $this->getWarningList(basename($testFile)); + $failures = $this->generateFailureMessages($file, $testFile); + $failureMessages = array_merge($failureMessages, $failures); + }//end foreach - if (is_array($expectedErrors) === false) { - throw new PHP_CodeSniffer_Exception('getErrorList() must return an array'); + if ($multiFileSniff === true) { + try { + self::$phpcs->process($testFiles, $standardName, array($sniffClass)); + } catch (Exception $e) { + $this->fail('An unexpected exception has been caught: '.$e->getMessage()); } - if (is_array($expectedWarnings) === false) { - throw new PHP_CodeSniffer_Exception('getWarningList() must return an array'); + $files = self::$phpcs->getFiles(); + foreach ($files as $file) { + $failures = $this->generateFailureMessages($file); + $failureMessages = array_merge($failureMessages, $failures); } + } - /* - We merge errors and warnings together to make it easier - to iterate over them and produce the errors string. In this way, - we can report on errors and warnings in the same line even though - it's not really structured to allow that. - */ - - $allProblems = array(); - - foreach ($foundErrors as $line => $lineErrors) { - foreach ($lineErrors as $column => $errors) { - if (isset($allProblems[$line]) === false) { - $allProblems[$line] = array( - 'expected_errors' => 0, - 'expected_warnings' => 0, - 'found_errors' => array(), - 'found_warnings' => array(), - ); - } + if (empty($failureMessages) === false) { + $this->fail(implode(PHP_EOL, $failureMessages)); + } - $allProblems[$line]['found_errors'] = array_merge($allProblems[$line]['found_errors'], $errors); - } + }//end testSniff() - if (isset($expectedErrors[$line]) === true) { - $allProblems[$line]['expected_errors'] = $expectedErrors[$line]; - } else { - $allProblems[$line]['expected_errors'] = 0; - } - unset($expectedErrors[$line]); - }//end foreach + /** + * Generate a list of test failures for a given sniffed file. + * + * @return array + * @throws PHP_CodeSniffer_Exception + */ + public function generateFailureMessages($file) + { + $testFile = $file->getFilename(); + + $foundErrors = $file->getErrors(); + $foundWarnings = $file->getWarnings(); + $expectedErrors = $this->getErrorList(basename($testFile)); + $expectedWarnings = $this->getWarningList(basename($testFile)); + + if (is_array($expectedErrors) === false) { + throw new PHP_CodeSniffer_Exception('getErrorList() must return an array'); + } + + if (is_array($expectedWarnings) === false) { + throw new PHP_CodeSniffer_Exception('getWarningList() must return an array'); + } + + /* + We merge errors and warnings together to make it easier + to iterate over them and produce the errors string. In this way, + we can report on errors and warnings in the same line even though + it's not really structured to allow that. + */ + + $allProblems = array(); + $failureMessages = array(); - foreach ($expectedErrors as $line => $numErrors) { + foreach ($foundErrors as $line => $lineErrors) { + foreach ($lineErrors as $column => $errors) { if (isset($allProblems[$line]) === false) { $allProblems[$line] = array( 'expected_errors' => 0, @@ -183,33 +211,33 @@ protected final function runTest() ); } - $allProblems[$line]['expected_errors'] = $numErrors; + $allProblems[$line]['found_errors'] = array_merge($allProblems[$line]['found_errors'], $errors); } - foreach ($foundWarnings as $line => $lineWarnings) { - foreach ($lineWarnings as $column => $warnings) { - if (isset($allProblems[$line]) === false) { - $allProblems[$line] = array( - 'expected_errors' => 0, - 'expected_warnings' => 0, - 'found_errors' => array(), - 'found_warnings' => array(), - ); - } + if (isset($expectedErrors[$line]) === true) { + $allProblems[$line]['expected_errors'] = $expectedErrors[$line]; + } else { + $allProblems[$line]['expected_errors'] = 0; + } - $allProblems[$line]['found_warnings'] = $warnings; - } + unset($expectedErrors[$line]); + }//end foreach - if (isset($expectedWarnings[$line]) === true) { - $allProblems[$line]['expected_warnings'] = $expectedWarnings[$line]; - } else { - $allProblems[$line]['expected_warnings'] = 0; - } + foreach ($expectedErrors as $line => $numErrors) { + if (isset($allProblems[$line]) === false) { + $allProblems[$line] = array( + 'expected_errors' => 0, + 'expected_warnings' => 0, + 'found_errors' => array(), + 'found_warnings' => array(), + ); + } - unset($expectedWarnings[$line]); - }//end foreach + $allProblems[$line]['expected_errors'] = $numErrors; + } - foreach ($expectedWarnings as $line => $numWarnings) { + foreach ($foundWarnings as $line => $lineWarnings) { + foreach ($lineWarnings as $column => $warnings) { if (isset($allProblems[$line]) === false) { $allProblems[$line] = array( 'expected_errors' => 0, @@ -219,73 +247,92 @@ protected final function runTest() ); } - $allProblems[$line]['expected_warnings'] = $numWarnings; + $allProblems[$line]['found_warnings'] = $warnings; } - // Order the messages by line number. - ksort($allProblems); - - foreach ($allProblems as $line => $problems) { - $numErrors = count($problems['found_errors']); - $numWarnings = count($problems['found_warnings']); - $expectedErrors = $problems['expected_errors']; - $expectedWarnings = $problems['expected_warnings']; - - $errors = ''; - $foundString = ''; - - if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) { - $lineMessage = "[LINE $line]"; - $expectedMessage = 'Expected '; - $foundMessage = 'in '.basename($testFile).' but found '; - - if ($expectedErrors !== $numErrors) { - $expectedMessage .= "$expectedErrors error(s)"; - $foundMessage .= "$numErrors error(s)"; - if ($numErrors !== 0) { - $foundString .= 'error(s)'; - $errors .= implode(PHP_EOL.' -> ', $problems['found_errors']); - } + if (isset($expectedWarnings[$line]) === true) { + $allProblems[$line]['expected_warnings'] = $expectedWarnings[$line]; + } else { + $allProblems[$line]['expected_warnings'] = 0; + } - if ($expectedWarnings !== $numWarnings) { - $expectedMessage .= ' and '; - $foundMessage .= ' and '; - if ($numWarnings !== 0) { - if ($foundString !== '') { - $foundString .= ' and '; - } - } - } + unset($expectedWarnings[$line]); + }//end foreach + + foreach ($expectedWarnings as $line => $numWarnings) { + if (isset($allProblems[$line]) === false) { + $allProblems[$line] = array( + 'expected_errors' => 0, + 'expected_warnings' => 0, + 'found_errors' => array(), + 'found_warnings' => array(), + ); + } + + $allProblems[$line]['expected_warnings'] = $numWarnings; + } + + // Order the messages by line number. + ksort($allProblems); + + foreach ($allProblems as $line => $problems) { + $numErrors = count($problems['found_errors']); + $numWarnings = count($problems['found_warnings']); + $expectedErrors = $problems['expected_errors']; + $expectedWarnings = $problems['expected_warnings']; + + $errors = ''; + $foundString = ''; + + if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) { + $lineMessage = "[LINE $line]"; + $expectedMessage = 'Expected '; + $foundMessage = 'in '.basename($testFile).' but found '; + + if ($expectedErrors !== $numErrors) { + $expectedMessage .= "$expectedErrors error(s)"; + $foundMessage .= "$numErrors error(s)"; + if ($numErrors !== 0) { + $foundString .= 'error(s)'; + $errors .= implode(PHP_EOL.' -> ', $problems['found_errors']); } if ($expectedWarnings !== $numWarnings) { - $expectedMessage .= "$expectedWarnings warning(s)"; - $foundMessage .= "$numWarnings warning(s)"; + $expectedMessage .= ' and '; + $foundMessage .= ' and '; if ($numWarnings !== 0) { - $foundString .= 'warning(s)'; - if (empty($errors) === false) { - $errors .= PHP_EOL.' -> '; + if ($foundString !== '') { + $foundString .= ' and '; } - - $errors .= implode(PHP_EOL.' -> ', $problems['found_warnings']); } } + } - $fullMessage = "$lineMessage $expectedMessage $foundMessage."; - if ($errors !== '') { - $fullMessage .= " The $foundString found were:".PHP_EOL." -> $errors"; + if ($expectedWarnings !== $numWarnings) { + $expectedMessage .= "$expectedWarnings warning(s)"; + $foundMessage .= "$numWarnings warning(s)"; + if ($numWarnings !== 0) { + $foundString .= 'warning(s)'; + if (empty($errors) === false) { + $errors .= PHP_EOL.' -> '; + } + + $errors .= implode(PHP_EOL.' -> ', $problems['found_warnings']); } + } - $failureMessages[] = $fullMessage; + $fullMessage = "$lineMessage $expectedMessage $foundMessage."; + if ($errors !== '') { + $fullMessage .= " The $foundString found were:".PHP_EOL." -> $errors"; } - }//end foreach - } - if (empty($failureMessages) === false) { - $this->fail(implode(PHP_EOL, $failureMessages)); - } + $failureMessages[] = $fullMessage; + }//end if + }//end foreach - }//end testSniff() + return $failureMessages; + + }//end generateFailureMessages() /**