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