Completed
Push — in-portal ( f639b3...6338fb )
by Alexander
03:49 queued 01:51
created

AbstractSniffUnitTest::getCliValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * An abstract class that all sniff unit tests must extend.
4
 *
5
 * PHP version 5
6
 *
7
 * @category  PHP
8
 * @package   PHP_CodeSniffer
9
 * @author    Greg Sherwood <[email protected]>
10
 * @author    Marc McIntyre <[email protected]>
11
 * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
12
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
13
 * @link      http://pear.php.net/package/PHP_CodeSniffer
14
 */
15
16
/**
17
 * An abstract class that all sniff unit tests must extend.
18
 *
19
 * A sniff unit test checks a .inc file for expected violations of a single
20
 * coding standard. Expected errors and warnings that are not found, or
21
 * warnings and errors that are not expected, are considered test failures.
22
 *
23
 * @category  PHP
24
 * @package   PHP_CodeSniffer
25
 * @author    Greg Sherwood <[email protected]>
26
 * @author    Marc McIntyre <[email protected]>
27
 * @copyright 2006-2012 Squiz Pty Ltd (ABN 77 084 670 600)
28
 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
29
 * @version   Release: @package_version@
30
 * @link      http://pear.php.net/package/PHP_CodeSniffer
31
 */
32
abstract class AbstractSniffUnitTest extends PHPUnit_Framework_TestCase
33
{
34
35
    /**
36
     * Enable or disable the backup and restoration of the $GLOBALS array.
37
     * Overwrite this attribute in a child class of TestCase.
38
     * Setting this attribute in setUp() has no effect!
39
     *
40
     * @var boolean
41
     */
42
    protected $backupGlobals = false;
43
44
    /**
45
     * The PHP_CodeSniffer object used for testing.
46
     *
47
     * @var PHP_CodeSniffer
48
     */
49
    protected static $phpcs = null;
50
51
52
    /**
53
     * Sets up this unit test.
54
     *
55
     * @return void
56
     */
57
    protected function setUp()
58
    {
59
        if (self::$phpcs === null) {
60
            self::$phpcs = new PHP_CodeSniffer();
61
            PHP_CodeSniffer::setConfigData('installed_paths', STANDARDS_PATH, true);
62
63
            // Conflicts with Composer AutoLoader.
64
            spl_autoload_unregister(array('PHP_CodeSniffer', 'autoload'));
65
        }
66
67
    }//end setUp()
68
69
70
    /**
71
     * Tests the extending classes Sniff class.
72
     *
73
     * @return void
74
     */
75
    public final function testSniff()
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
76
    {
77
        // Skip this test if we can't run in this environment.
78
        if ($this->shouldSkipTest() === true) {
79
            $this->markTestSkipped();
80
        }
81
82
        if (method_exists(self::$phpcs, 'initStandard') === true) {
83
            // Only init standard without processing it's files, when possible.
84
            self::$phpcs->initStandard($this->getStandardName(), array($this->getSniffCode()));
85
        } else {
86
            // Init standard and process all it's files.
87
            self::$phpcs->process(array(), $this->getStandardName(), array($this->getSniffCode()));
88
        }
89
90
        self::$phpcs->setIgnorePatterns(array());
91
92
        $failureMessages = array();
93
        $fixingSupported = $this->isFixingSupported();
94
        foreach ($this->_getTestFiles() as $testFile) {
95
            $filename = basename($testFile);
96
97
            try {
98
                $cliValues = $this->getCliValues($filename);
99
                self::$phpcs->cli->setCommandLineValues($cliValues);
100
                $phpcsFile       = self::$phpcs->processFile($testFile);
101
                $failureMessages = array_merge($failureMessages, $this->generateFailureMessages($phpcsFile));
102
103
                if ($fixingSupported === true && $phpcsFile->getFixableCount() > 0) {
104
                    // Attempt to fix the errors.
105
                    $phpcsFile->fixer->fixFile();
106
                    $fixable = $phpcsFile->getFixableCount();
107
                    if ($fixable > 0) {
108
                        $failureMessages[] = "Failed to fix $fixable fixable violations in $filename.";
109
                    }
110
111
                    // Check for a .fixed file to check for accuracy of fixes.
112
                    $fixedFile = $testFile.'.fixed';
113
                    if (file_exists($fixedFile) === true) {
114
                        $diff = $phpcsFile->fixer->generateDiff($fixedFile);
115
                        if (trim($diff) !== '') {
116
                            $filename          = basename($testFile);
117
                            $fixedFilename     = basename($fixedFile);
118
                            $failureMessages[] = "Fixed version of $filename does not match expected version in $fixedFilename; the diff is\n$diff";
119
                        }
120
                    }
121
                }
122
            } catch (Exception $e) {
123
                $this->fail('An unexpected exception has been caught: '.$e->getMessage());
124
            }//end try
125
        }//end foreach
126
127
        if (empty($failureMessages) === false) {
128
            $this->fail(implode(PHP_EOL, $failureMessages));
129
        }
130
131
    }//end testStandard()
132
133
134
    /**
135
     * Determines if used PHPCS version supports fixing.
136
     *
137
     * @return bool
138
     */
139
    protected function isFixingSupported()
140
    {
141
        $classReflection = new \ReflectionClass('PHP_CodeSniffer_File');
142
143
        return $classReflection->hasProperty('fixer');
144
145
    }//end isFixingSupported()
146
147
148
    /**
149
     * Should this test be skipped for some reason.
150
     *
151
     * @return bool
152
     */
153
    protected function shouldSkipTest()
154
    {
155
        return false;
156
157
    }//end shouldSkipTest()
158
159
160
    /**
161
     * The name of the coding standard we are testing.
162
     *
163
     * @return string
164
     */
165
    protected function getStandardName()
166
    {
167
        $basename = $this->getBaseName();
168
169
        return STANDARDS_PATH.DIRECTORY_SEPARATOR.substr($basename, 0, strpos($basename, '_'));
170
171
    }//end getStandardName()
172
173
174
    /**
175
     * The code of the sniff we are testing.
176
     *
177
     * @return string
178
     */
179
    protected function getSniffCode()
180
    {
181
        $parts = explode('_', $this->getBaseName());
182
183
        return $parts[0].'.'.$parts[2].'.'.$parts[3];
184
185
    }//end getSniffCode()
186
187
188
    /**
189
     * Get a list of all test files to check. These will have the same base name
190
     * but different extensions. We ignore the .php file as it is the class.
191
     *
192
     * @return array
193
     */
194
    private function _getTestFiles()
195
    {
196
        // The basis for determining file locations.
197
        $basename = $this->getBaseName();
198
199
        // The name of the dummy file we are testing.
200
        $testFileBase = STANDARDS_PATH.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $basename).'UnitTest.';
201
202
        $testFiles = array();
203
204
        /** @var SplFileInfo $file */
205
        $directoryIterator = new DirectoryIterator(dirname($testFileBase));
206
207
        foreach ($directoryIterator as $file) {
208
            $path = $file->getPathname();
209
210
            if (substr($path, 0, strlen($testFileBase)) === $testFileBase && $path !== $testFileBase.'php' && $file->getExtension() !== 'fixed') {
211
                $testFiles[] = $path;
212
            }
213
        }
214
215
        // Get them in order.
216
        sort($testFiles);
217
218
        return $testFiles;
219
220
    }//end _getTestFiles()
221
222
223
    /**
224
     * The basis for determining file locations.
225
     *
226
     * @return string
227
     */
228
    protected function getBaseName()
229
    {
230
        return substr(get_class($this), 0, -8);
231
232
    }//end getBaseName()
233
234
235
    /**
236
     * Generate a list of test failures for a given sniffed file.
237
     *
238
     * @param PHP_CodeSniffer_File $file The file being tested.
239
     *
240
     * @return array
241
     * @throws PHP_CodeSniffer_Exception
242
     */
243
    public function generateFailureMessages(PHP_CodeSniffer_File $file)
244
    {
245
        $testFile = $file->getFilename();
246
247
        $foundErrors      = $file->getErrors();
248
        $foundWarnings    = $file->getWarnings();
249
        $expectedErrors   = $this->getErrorList(basename($testFile));
250
        $expectedWarnings = $this->getWarningList(basename($testFile));
251
252
        if (is_array($expectedErrors) === false) {
253
            throw new PHP_CodeSniffer_Exception('getErrorList() must return an array');
254
        }
255
256
        if (is_array($expectedWarnings) === false) {
257
            throw new PHP_CodeSniffer_Exception('getWarningList() must return an array');
258
        }
259
260
        /*
261
            We merge errors and warnings together to make it easier
262
            to iterate over them and produce the errors string. In this way,
263
            we can report on errors and warnings in the same line even though
264
            it's not really structured to allow that.
265
        */
266
267
        $allProblems     = array();
268
        $failureMessages = array();
269
270
        foreach ($foundErrors as $line => $lineErrors) {
271
            foreach ($lineErrors as $column => $errors) {
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(),
278
                                          );
279
                }
280
281
                $foundErrorsTemp = array();
282
                foreach ($allProblems[$line]['found_errors'] as $foundError) {
283
                    $foundErrorsTemp[] = $foundError;
284
                }
285
286
                $errorsTemp = array();
287
                foreach ($errors as $foundError) {
288
                    $errorsTemp[] = $foundError['message'].' ('.$foundError['source'].')';
289
                }
290
291
                $allProblems[$line]['found_errors'] = array_merge($foundErrorsTemp, $errorsTemp);
292
            }//end foreach
293
294
            if (isset($expectedErrors[$line]) === true) {
295
                $allProblems[$line]['expected_errors'] = $expectedErrors[$line];
296
            } else {
297
                $allProblems[$line]['expected_errors'] = 0;
298
            }
299
300
            unset($expectedErrors[$line]);
301
        }//end foreach
302
303
        foreach ($expectedErrors as $line => $numErrors) {
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(),
310
                                      );
311
            }
312
313
            $allProblems[$line]['expected_errors'] = $numErrors;
314
        }
315
316
        foreach ($foundWarnings as $line => $lineWarnings) {
317
            foreach ($lineWarnings as $column => $warnings) {
318
                if (isset($allProblems[$line]) === false) {
319
                    $allProblems[$line] = array(
320
                                           'expected_errors'   => 0,
321
                                           'expected_warnings' => 0,
322
                                           'found_errors'      => array(),
323
                                           'found_warnings'    => array(),
324
                                          );
325
                }
326
327
                $foundWarningsTemp = array();
328
                foreach ($allProblems[$line]['found_warnings'] as $foundWarning) {
329
                    $foundWarningsTemp[] = $foundWarning;
330
                }
331
332
                $warningsTemp = array();
333
                foreach ($warnings as $warning) {
334
                    $warningsTemp[] = $warning['message'].' ('.$warning['source'].')';
335
                }
336
337
                $allProblems[$line]['found_warnings'] = array_merge($foundWarningsTemp, $warningsTemp);
338
            }//end foreach
339
340
            if (isset($expectedWarnings[$line]) === true) {
341
                $allProblems[$line]['expected_warnings'] = $expectedWarnings[$line];
342
            } else {
343
                $allProblems[$line]['expected_warnings'] = 0;
344
            }
345
346
            unset($expectedWarnings[$line]);
347
        }//end foreach
348
349
        foreach ($expectedWarnings as $line => $numWarnings) {
350
            if (isset($allProblems[$line]) === false) {
351
                $allProblems[$line] = array(
352
                                       'expected_errors'   => 0,
353
                                       'expected_warnings' => 0,
354
                                       'found_errors'      => array(),
355
                                       'found_warnings'    => array(),
356
                                      );
357
            }
358
359
            $allProblems[$line]['expected_warnings'] = $numWarnings;
360
        }
361
362
        // Order the messages by line number.
363
        ksort($allProblems);
364
365
        foreach ($allProblems as $line => $problems) {
366
            $numErrors        = count($problems['found_errors']);
367
            $numWarnings      = count($problems['found_warnings']);
368
            $expectedErrors   = $problems['expected_errors'];
369
            $expectedWarnings = $problems['expected_warnings'];
370
371
            $errors      = '';
372
            $foundString = '';
373
374
            if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) {
375
                $lineMessage     = "[LINE $line]";
376
                $expectedMessage = 'Expected ';
377
                $foundMessage    = 'in '.basename($testFile).' but found ';
378
379
                if ($expectedErrors !== $numErrors) {
380
                    $expectedMessage .= "$expectedErrors error(s)";
381
                    $foundMessage    .= "$numErrors error(s)";
382
                    if ($numErrors !== 0) {
383
                        $foundString .= 'error(s)';
384
                        $errors      .= implode(PHP_EOL.' -> ', $problems['found_errors']);
385
                    }
386
387
                    if ($expectedWarnings !== $numWarnings) {
388
                        $expectedMessage .= ' and ';
389
                        $foundMessage    .= ' and ';
390
                        if ($numWarnings !== 0) {
391
                            if ($foundString !== '') {
392
                                $foundString .= ' and ';
393
                            }
394
                        }
395
                    }
396
                }
397
398
                if ($expectedWarnings !== $numWarnings) {
399
                    $expectedMessage .= "$expectedWarnings warning(s)";
400
                    $foundMessage    .= "$numWarnings warning(s)";
401
                    if ($numWarnings !== 0) {
402
                        $foundString .= 'warning(s)';
403
                        if (empty($errors) === false) {
404
                            $errors .= PHP_EOL.' -> ';
405
                        }
406
407
                        $errors .= implode(PHP_EOL.' -> ', $problems['found_warnings']);
408
                    }
409
                }
410
411
                $fullMessage = "$lineMessage $expectedMessage $foundMessage.";
412
                if ($errors !== '') {
413
                    $fullMessage .= " The $foundString found were:".PHP_EOL." -> $errors";
414
                }
415
416
                $failureMessages[] = $fullMessage;
417
            }//end if
418
        }//end foreach
419
420
        return $failureMessages;
421
422
    }//end generateFailureMessages()
423
424
425
    /**
426
     * Get a list of CLI values to set before the file is tested.
427
     *
428
     * @param string $filename The name of the file being tested.
429
     *
430
     * @return array
431
     */
432
    public function getCliValues($filename)
433
    {
434
        return array();
435
436
    }//end getCliValues()
437
438
439
    /**
440
     * Returns the lines where errors should occur.
441
     *
442
     * The key of the array should represent the line number and the value
443
     * should represent the number of errors that should occur on that line.
444
     *
445
     * @param string $testFile Name of the file with test data.
446
     *
447
     * @return array(int => int)
448
     */
449
    protected abstract function getErrorList($testFile);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
450
451
452
    /**
453
     * Returns the lines where warnings should occur.
454
     *
455
     * The key of the array should represent the line number and the value
456
     * should represent the number of warnings that should occur on that line.
457
     *
458
     * @param string $testFile Name of the file with test data.
459
     *
460
     * @return array(int => int)
461
     */
462
    protected abstract function getWarningList($testFile);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
463
464
465
}//end class
466