3
* An abstract class that all sniff unit tests must extend.
8
* @package PHP_CodeSniffer
9
* @author Greg Sherwood <gsherwood@squiz.net>
10
* @author Marc McIntyre <mmcintyre@squiz.net>
11
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
12
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
13
* @link http://pear.php.net/package/PHP_CodeSniffer
16
require_once 'PHPUnit/Framework/TestCase.php';
19
* An abstract class that all sniff unit tests must extend.
21
* A sniff unit test checks a .inc file for expected violations of a single
22
* coding standard. Expected errors and warnings that are not found, or
23
* warnings and errors that are not expected, are considered test failures.
26
* @package PHP_CodeSniffer
27
* @author Greg Sherwood <gsherwood@squiz.net>
28
* @author Marc McIntyre <mmcintyre@squiz.net>
29
* @copyright 2006-2011 Squiz Pty Ltd (ABN 77 084 670 600)
30
* @license http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
31
* @version Release: 1.3.4
32
* @link http://pear.php.net/package/PHP_CodeSniffer
34
abstract class AbstractSniffUnitTest extends PHPUnit_Framework_TestCase
38
* The PHP_CodeSniffer object used for testing.
40
* @var PHP_CodeSniffer
42
protected static $phpcs = null;
46
* Sets up this unit test.
50
protected function setUp()
52
if (self::$phpcs === null) {
53
self::$phpcs = new PHP_CodeSniffer();
60
* Should this test be skipped for some reason.
64
protected function shouldSkipTest()
68
}//end shouldSkipTest()
72
* Tests the extending classes Sniff class.
75
* @throws PHPUnit_Framework_Error
77
protected final function runTest()
79
// Skip this test if we can't run in this environment.
80
if ($this->shouldSkipTest() === true) {
81
$this->markTestSkipped();
84
// The basis for determining file locations.
85
$basename = substr(get_class($this), 0, -8);
87
// The name of the coding standard we are testing.
88
$standardName = substr($basename, 0, strpos($basename, '_'));
90
// The class name of the sniff we are testing.
91
$sniffClass = str_replace('_Tests_', '_Sniffs_', $basename).'Sniff';
93
if (is_file(dirname(__FILE__).'/../../CodeSniffer.php') === true) {
94
// We have not been installed.
95
$standardsDir = realpath(dirname(__FILE__).'/../../CodeSniffer/Standards');
96
$testFileBase = $standardsDir.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $basename).'UnitTest.';
98
// The name of the dummy file we are testing.
99
$testFileBase = dirname(__FILE__).DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $basename).'UnitTest.';
102
// Get a list of all test files to check. These will have the same base
103
// name but different extensions. We ignore the .php file as it is the
105
$testFiles = array();
107
$dir = substr($testFileBase, 0, strrpos($testFileBase, DIRECTORY_SEPARATOR));
108
$di = new DirectoryIterator($dir);
110
foreach ($di as $file) {
111
$path = $file->getPathname();
112
if (substr($path, 0, strlen($testFileBase)) === $testFileBase) {
113
if ($path !== $testFileBase.'php') {
114
$testFiles[] = $path;
119
// Get them in order. This is particularly important for multi-file sniffs.
122
$failureMessages = array();
123
$multiFileSniff = false;
124
foreach ($testFiles as $testFile) {
126
self::$phpcs->process(array(), $standardName, array($sniffClass));
127
self::$phpcs->setIgnorePatterns(array());
128
self::$phpcs->processFile($testFile);
129
self::$phpcs->processMulti();
130
} catch (Exception $e) {
131
$this->fail('An unexpected exception has been caught: '.$e->getMessage());
134
// After processing a file, check if the sniff was actually
135
// a multi-file sniff (i.e., had no individual file sniffs).
136
// If it is, we can skip checking of the other files and
137
// do a single multi-file check.
138
$sniffs = self::$phpcs->getTokenSniffs();
139
if (empty($sniffs['file']) === true) {
140
$multiFileSniff = true;
144
$files = self::$phpcs->getFiles();
145
if (empty($files) === true) {
146
// File was skipped for some reason.
147
echo "Skipped: $testFile\n";
148
$this->markTestSkipped();
151
$file = array_pop($files);
153
$failures = $this->generateFailureMessages($file);
154
$failureMessages = array_merge($failureMessages, $failures);
157
if ($multiFileSniff === true) {
159
self::$phpcs->process(array(), $standardName, array($sniffClass));
160
self::$phpcs->setIgnorePatterns(array());
161
foreach ($testFiles as $testFile) {
162
self::$phpcs->processFile($testFile);
165
self::$phpcs->processMulti();
166
} catch (Exception $e) {
167
$this->fail('An unexpected exception has been caught: '.$e->getMessage());
170
$files = self::$phpcs->getFiles();
171
if (empty($files) === true) {
172
// File was skipped for some reason.
173
$this->markTestSkipped();
175
foreach ($files as $file) {
176
$failures = $this->generateFailureMessages($file);
177
$failureMessages = array_merge($failureMessages, $failures);
182
if (empty($failureMessages) === false) {
183
$this->fail(implode(PHP_EOL, $failureMessages));
190
* Generate a list of test failures for a given sniffed file.
192
* @param PHP_CodeSniffer_File $file The file being tested.
195
* @throws PHP_CodeSniffer_Exception
197
public function generateFailureMessages(PHP_CodeSniffer_File $file)
199
$testFile = $file->getFilename();
201
$foundErrors = $file->getErrors();
202
$foundWarnings = $file->getWarnings();
203
$expectedErrors = $this->getErrorList(basename($testFile));
204
$expectedWarnings = $this->getWarningList(basename($testFile));
206
if (is_array($expectedErrors) === false) {
207
throw new PHP_CodeSniffer_Exception('getErrorList() must return an array');
210
if (is_array($expectedWarnings) === false) {
211
throw new PHP_CodeSniffer_Exception('getWarningList() must return an array');
215
We merge errors and warnings together to make it easier
216
to iterate over them and produce the errors string. In this way,
217
we can report on errors and warnings in the same line even though
218
it's not really structured to allow that.
221
$allProblems = array();
222
$failureMessages = array();
224
foreach ($foundErrors as $line => $lineErrors) {
225
foreach ($lineErrors as $column => $errors) {
226
if (isset($allProblems[$line]) === false) {
227
$allProblems[$line] = array(
228
'expected_errors' => 0,
229
'expected_warnings' => 0,
230
'found_errors' => array(),
231
'found_warnings' => array(),
235
$foundErrorsTemp = array();
236
foreach ($allProblems[$line]['found_errors'] as $foundError) {
237
$foundErrorsTemp[] = $foundError;
240
$errorsTemp = array();
241
foreach ($errors as $foundError) {
242
$errorsTemp[] = $foundError['message'];
245
$allProblems[$line]['found_errors'] = array_merge($foundErrorsTemp, $errorsTemp);
248
if (isset($expectedErrors[$line]) === true) {
249
$allProblems[$line]['expected_errors'] = $expectedErrors[$line];
251
$allProblems[$line]['expected_errors'] = 0;
254
unset($expectedErrors[$line]);
257
foreach ($expectedErrors as $line => $numErrors) {
258
if (isset($allProblems[$line]) === false) {
259
$allProblems[$line] = array(
260
'expected_errors' => 0,
261
'expected_warnings' => 0,
262
'found_errors' => array(),
263
'found_warnings' => array(),
267
$allProblems[$line]['expected_errors'] = $numErrors;
270
foreach ($foundWarnings as $line => $lineWarnings) {
271
foreach ($lineWarnings as $column => $warnings) {
272
if (isset($allProblems[$line]) === false) {
273
$allProblems[$line] = array(
274
'expected_errors' => 0,
275
'expected_warnings' => 0,
276
'found_errors' => array(),
277
'found_warnings' => array(),
281
$foundWarningsTemp = array();
282
foreach ($allProblems[$line]['found_warnings'] as $foundWarning) {
283
$foundWarningsTemp[] = $foundWarning;
286
$warningsTemp = array();
287
foreach ($warnings as $warning) {
288
$warningsTemp[] = $warning['message'];
291
$allProblems[$line]['found_warnings'] = array_merge($foundWarningsTemp, $warningsTemp);
294
if (isset($expectedWarnings[$line]) === true) {
295
$allProblems[$line]['expected_warnings'] = $expectedWarnings[$line];
297
$allProblems[$line]['expected_warnings'] = 0;
300
unset($expectedWarnings[$line]);
303
foreach ($expectedWarnings as $line => $numWarnings) {
304
if (isset($allProblems[$line]) === false) {
305
$allProblems[$line] = array(
306
'expected_errors' => 0,
307
'expected_warnings' => 0,
308
'found_errors' => array(),
309
'found_warnings' => array(),
313
$allProblems[$line]['expected_warnings'] = $numWarnings;
316
// Order the messages by line number.
319
foreach ($allProblems as $line => $problems) {
320
$numErrors = count($problems['found_errors']);
321
$numWarnings = count($problems['found_warnings']);
322
$expectedErrors = $problems['expected_errors'];
323
$expectedWarnings = $problems['expected_warnings'];
328
if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) {
329
$lineMessage = "[LINE $line]";
330
$expectedMessage = 'Expected ';
331
$foundMessage = 'in '.basename($testFile).' but found ';
333
if ($expectedErrors !== $numErrors) {
334
$expectedMessage .= "$expectedErrors error(s)";
335
$foundMessage .= "$numErrors error(s)";
336
if ($numErrors !== 0) {
337
$foundString .= 'error(s)';
338
$errors .= implode(PHP_EOL.' -> ', $problems['found_errors']);
341
if ($expectedWarnings !== $numWarnings) {
342
$expectedMessage .= ' and ';
343
$foundMessage .= ' and ';
344
if ($numWarnings !== 0) {
345
if ($foundString !== '') {
346
$foundString .= ' and ';
352
if ($expectedWarnings !== $numWarnings) {
353
$expectedMessage .= "$expectedWarnings warning(s)";
354
$foundMessage .= "$numWarnings warning(s)";
355
if ($numWarnings !== 0) {
356
$foundString .= 'warning(s)';
357
if (empty($errors) === false) {
358
$errors .= PHP_EOL.' -> ';
361
$errors .= implode(PHP_EOL.' -> ', $problems['found_warnings']);
365
$fullMessage = "$lineMessage $expectedMessage $foundMessage.";
366
if ($errors !== '') {
367
$fullMessage .= " The $foundString found were:".PHP_EOL." -> $errors";
370
$failureMessages[] = $fullMessage;
374
return $failureMessages;
376
}//end generateFailureMessages()
380
* Returns the lines where errors should occur.
382
* The key of the array should represent the line number and the value
383
* should represent the number of errors that should occur on that line.
385
* @return array(int => int)
387
protected abstract function getErrorList();
391
* Returns the lines where warnings should occur.
393
* The key of the array should represent the line number and the value
394
* should represent the number of warnings that should occur on that line.
396
* @return array(int => int)
398
protected abstract function getWarningList();