diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 528e67a8..7e86aa36 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -22,6 +22,9 @@ + + + diff --git a/Yoast/Sniffs/Files/TestDoublesSniff.php b/Yoast/Sniffs/Files/TestDoublesSniff.php new file mode 100644 index 00000000..cb65af1d --- /dev/null +++ b/Yoast/Sniffs/Files/TestDoublesSniff.php @@ -0,0 +1,159 @@ +getFileName() ); + + if ( 'STDIN' === $file ) { + return; + } + + $object_name = $phpcsFile->getDeclarationName( $stackPtr ); + if ( empty( $object_name ) ) { + return; + } + + if ( stripos( $object_name, 'mock' ) === false && stripos( $object_name, 'double' ) === false ) { + return; + } + + if ( ! isset( $phpcsFile->config->basepath ) ) { + $phpcsFile->addWarning( + 'For the TestDoubles sniff to be able to function, the --basepath needs to be set.', + 0, + 'MissingBasePath' + ); + + return ( $phpcsFile->numTokens + 1 ); + } + + $base_path = $this->normalize_directory_separators( $phpcsFile->config->basepath ); + if ( ! isset( $this->target_path ) || defined( 'PHP_CODESNIFFER_IN_TESTS' ) ) { + $target_path = $base_path . '/'; + $target_path .= ltrim( $this->normalize_directory_separators( $this->doubles_path ), '/' ); + + $this->target_path = false; + if ( file_exists( $target_path ) && is_dir( $target_path ) ) { + $this->target_path = strtolower( $target_path ); + } + } + + if ( false === $this->target_path ) { + // Non-existent target path. + $phpcsFile->addError( + 'Double/Mock test helper class detected, but no "%s" sub-directory found in "%s". Please create the sub-directory.', + $stackPtr, + 'NoDoublesDirectory', + array( + $this->doubles_path, + $base_path, + ) + ); + } + + $path_info = pathinfo( $file ); + if ( empty( $path_info['dirname'] ) ) { + return; + } + + $tokens = $phpcsFile->getTokens(); + $dirname = $this->normalize_directory_separators( $path_info['dirname'] ); + if ( false === $this->target_path || stripos( $dirname, $this->target_path ) === false ) { + $phpcsFile->addError( + 'Double/Mock test helper classes should be placed in the "%s" sub-directory. Found %s: %s', + $stackPtr, + 'WrongDirectory', + array( + $this->doubles_path, + $tokens[ $stackPtr ]['content'], + $object_name, + ) + ); + } + + $more_objects_in_file = $phpcsFile->findNext( $this->register(), ( $stackPtr + 1 ) ); + if ( false !== $more_objects_in_file ) { + $phpcsFile->addError( + 'Double/Mock test helper classes should be in their own file. Found %s: %s', + $stackPtr, + 'OneObjectPerFile', + array( + $tokens[ $stackPtr ]['content'], + $object_name, + ) + ); + } + } + + /** + * Normalize all directory separators to be a forward slash. + * + * @param string $path Path to normalize. + * + * @return string + */ + private function normalize_directory_separators( $path ) { + return strtr( $path, '\\', '/' ); + } +} diff --git a/Yoast/Tests/Files/TestDoublesUnitTest.inc b/Yoast/Tests/Files/TestDoublesUnitTest.inc new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/Yoast/Tests/Files/TestDoublesUnitTest.inc @@ -0,0 +1 @@ +basepath = __DIR__ . DIRECTORY_SEPARATOR . 'TestDoublesUnitTests'; + } + + /** + * Get a list of all test files to check. + * + * @param string $testFileBase The base path that the unit tests files will have. + * + * @return string[] + */ + protected function getTestFiles( $testFileBase ) { + $sep = DIRECTORY_SEPARATOR; + $test_files = glob( dirname( $testFileBase ) . $sep . 'TestDoublesUnitTests{' . $sep . ',' . $sep . '*' . $sep . '}*.inc', GLOB_BRACE ); + + if ( ! empty( $test_files ) ) { + return $test_files; + } + + return array( $testFileBase . '.inc' ); + } + + /** + * Returns the lines where errors should occur. + * + * @param string $testFile The name of the file being tested. + * + * @return array => + */ + public function getErrorList( $testFile = '' ) { + + switch ( $testFile ) { + // In tests/. + case 'mock-not-in-correct-dir.inc': + return array( + 3 => 1, + ); + + case 'multiple-objects-in-file.inc': + return array( + 5 => 2, + ); + + case 'not-in-correct-custom-dir.inc': + return array( + 4 => 2, + ); + + case 'not-in-correct-dir-double.inc': + return array( + 3 => 1, + ); + + case 'not-in-correct-dir-mock.inc': + return array( + 3 => 1, + ); + + case 'not-double-or-mock.inc': // In tests. + case 'correct-dir-double.inc': // In tests/doubles. + case 'correct-dir-mock.inc': // In tests/doubles. + case 'correct-custom-dir.inc': // In tests/mocks. + default: + return array(); + } + } + + /** + * Returns the lines where warnings should occur. + * + * @param string $testFile The name of the file being tested. + * + * @return array => + */ + public function getWarningList( $testFile = '' ) { + if ( $testFile === 'no-basepath.inc' ) { + return array( + 1 => 1, + ); + } + + return array(); + } +} diff --git a/Yoast/Tests/Files/TestDoublesUnitTests/tests/doubles/correct-dir-double.inc b/Yoast/Tests/Files/TestDoublesUnitTests/tests/doubles/correct-dir-double.inc new file mode 100644 index 00000000..0ef27d1c --- /dev/null +++ b/Yoast/Tests/Files/TestDoublesUnitTests/tests/doubles/correct-dir-double.inc @@ -0,0 +1,3 @@ +