From 8ec5629f20bfebf9512169c78d53f7005be5e3b2 Mon Sep 17 00:00:00 2001 From: Bill Mitchell Date: Sun, 11 Jun 2017 08:00:33 -0700 Subject: [PATCH] Fixes #1 --- .gitignore | 1 + .travis.yml | 4 +- composer.json | 10 +- coverage.xml | 14 -- .../CyclomaticComplexityAssessor.php | 130 ++++++++++++++++++ src/Class.php | 8 -- tests/BaseTestCase.php | 10 ++ tests/ExampleTest.php | 14 -- .../Assets/ClassWithForLoop.inc | 11 ++ .../Assets/ClassWithIfElseIf.inc | 13 ++ .../ClassWithManyMethodsAndLotsOfBranches.inc | 60 ++++++++ .../Assets/ClassWithOneEmptyMethod.inc | 9 ++ .../Assets/ClassWithOneMethodWithNestedIf.inc | 15 ++ .../Assets/ClassWithOneMethodWithOneIf.inc | 13 ++ .../Assets/ClassWithSwitchStatement.inc | 23 ++++ .../Assets/ClassWithWhileLoop.inc | 11 ++ .../Assets/EmptyClass.inc | 6 + .../CyclomaticComplexityAssessorTest.php | 75 ++++++++++ 18 files changed, 384 insertions(+), 43 deletions(-) delete mode 100644 coverage.xml create mode 100644 src/Assessors/CyclomaticComplexity/CyclomaticComplexityAssessor.php delete mode 100644 src/Class.php create mode 100644 tests/BaseTestCase.php delete mode 100644 tests/ExampleTest.php create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithForLoop.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithIfElseIf.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithManyMethodsAndLotsOfBranches.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithOneEmptyMethod.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithOneMethodWithNestedIf.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithOneMethodWithOneIf.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithSwitchStatement.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithWhileLoop.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/Assets/EmptyClass.inc create mode 100644 tests/Unit/Assessors/CyclomaticComplexity/CyclomaticComplexityAssessorTest.php diff --git a/.gitignore b/.gitignore index f02a2f86..91c770d6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build composer.lock docs vendor +coverage.xml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 5d326dee..3b418292 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ before_script: script: - vendor/bin/phpcs --standard=psr2 src - - vendor/bin/phpcs --standard=phpcs.xml src tests + - if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.0" ]]; then vendor/bin/phpcs --standard=phpcs.xml src --ignore=tests/Sniffs; fi - vendor/bin/phpcs --standard=codor.xml src -spn - vendor/bin/phpunit --debug --coverage-clover=coverage.xml - vendor/bin/phpmd src text codesize,unusedcode,naming @@ -20,4 +20,4 @@ script: after_success: - # - bash <(curl -s https://codecov.io/bash) \ No newline at end of file + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/composer.json b/composer.json index 6d1b897b..67193bc7 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,11 @@ { - "name": "bmitch/ChurnPhp", + "name": "bmitch/churn-php", "description": "Discover files in need of refactoring.", "keywords": [ "bmitch", - "ChurnPhp" + "churn-php" ], - "homepage": "https://github.com/bmitch/ChurnPhp", + "homepage": "https://github.com/bmitch/churn-php", "license": "MIT", "authors": [ { @@ -30,7 +30,7 @@ }, "autoload": { "psr-4": { - "Codor\\": "src/Churn/" + "Churn\\": "src" } }, "autoload-dev": { @@ -46,7 +46,7 @@ "vendor/bin/phpcs --standard=codor.xml src -spn", "vendor/bin/phpunit --debug --coverage-clover=coverage.xml", "vendor/bin/phpmd src text codesize,unusedcode,naming", - "vendor/bin/phploc src --progress", + "vendor/bin/phploc src", "vendor/bin/phpcpd src", "vendor/bin/phpunit" ] diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index 59583c22..00000000 --- a/coverage.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/Assessors/CyclomaticComplexity/CyclomaticComplexityAssessor.php b/src/Assessors/CyclomaticComplexity/CyclomaticComplexityAssessor.php new file mode 100644 index 00000000..bf47ecd6 --- /dev/null +++ b/src/Assessors/CyclomaticComplexity/CyclomaticComplexityAssessor.php @@ -0,0 +1,130 @@ +score = 0; + } + + /** + * Asses the files cyclomatic complexity. + * @param string $filePath Path and file name. + * @return integer + */ + public function assess($filePath): int + { + $contents = $this->getFileContents($filePath); + + $this->countTheMethods($contents); + $this->countTheIfStatements($contents); + $this->countTheElseIfStatements($contents); + $this->countTheWhileLoops($contents); + $this->countTheForLoops($contents); + $this->countTheCaseStatements($contents); + + if ($this->score == 0) { + $this->score = 1; + } + return $this->score; + } + + /** + * Count how many methods there are. + * @param string $contents Path and filename. + * @return void + */ + protected function countTheMethods($contents) + { + preg_match("/[ ]function[ ]/", $contents, $matches); + if (isset($matches[0])) { + $this->score ++; + } + } + + /** + * Count how many if statements there are. + * @param string $contents Path and filename. + * @return void + */ + protected function countTheIfStatements($contents) + { + $this->score += $this->howmAnyPatternMatches("/[ ]if[ ]/", $contents); + } + + /** + * Count how many else if statements there are. + * @param string $contents Path and filename. + * @return void + */ + protected function countTheElseIfStatements($contents) + { + $this->score += $this->howmAnyPatternMatches("/elseif/", $contents); + } + + /** + * Count how many while loops there are. + * @param string $contents Path and filename. + * @return void + */ + protected function countTheWhileLoops($contents) + { + $this->score += $this->howmAnyPatternMatches("/while/", $contents); + } + + /** + * Count how many for loops there are. + * @param string $contents Path and filename. + * @return void + */ + protected function countTheForLoops($contents) + { + $this->score += $this->howmAnyPatternMatches("/for/", $contents); + } + + /** + * Count how many case statements there are. + * @param string $contents Path and filename. + * @return void + */ + protected function countTheCaseStatements($contents) + { + $this->score += $this->howmAnyPatternMatches("/case/", $contents); + } + + /** + * For the given $pattern on $string, how many matches are returned? + * @param string $pattern Regex pattern. + * @param string $string Any string. + * @return integer + */ + protected function howManyPatternMatches($pattern, $string): int + { + preg_match_all($pattern, $string, $matches); + if (isset($matches[0])) { + return count($matches[0]); + } + return 0; + } + + /** + * Return the contents of the provided file at $filePath. + * @param string $filePath Path and filename. + * @return string + */ + protected function getFileContents($filePath): string + { + return file_get_contents($filePath); + } +} diff --git a/src/Class.php b/src/Class.php deleted file mode 100644 index 55339c98..00000000 --- a/src/Class.php +++ /dev/null @@ -1,8 +0,0 @@ -assertTrue(true); - } -} diff --git a/tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithForLoop.inc b/tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithForLoop.inc new file mode 100644 index 00000000..00d8f885 --- /dev/null +++ b/tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithForLoop.inc @@ -0,0 +1,11 @@ +assertEquals(1, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/EmptyClass.inc')); + } + + /** @test */ + public function a_class_with_one_empty_method_has_a_complexity_of_one() + { + $this->assertEquals(1, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithOneEmptyMethod.inc')); + } + + /** @test */ + public function a_class_with_a_method_containing_one_if_statement_has_a_complexity_of_two() + { + $this->assertEquals(2, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithOneMethodWithOneIf.inc')); + } + + /** @test */ + public function a_class_with_a_method_containing_a_nested_if_statement_has_a_complexity_of_three() + { + $this->assertEquals(3, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithOneMethodWithNestedIf.inc')); + } + + /** @test */ + public function a_class_with_a_method_containing_an_if_else_if_statement_has_a_complexity_of_three() + { + $this->assertEquals(3, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithIfElseIf.inc')); + } + + /** @test */ + public function a_class_with_a_method_containing_a_while_loop_has_a_complexity_of_two() + { + $this->assertEquals(2, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithWhileLoop.inc')); + } + + /** @test */ + public function a_class_with_a_method_containing_a_for_loop_has_a_complexity_of_two() + { + $this->assertEquals(2, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithForLoop.inc')); + } + + /** @test */ + public function a_class_with_a_method_a_switch_statement_with_three_cases_has_a_complexity_of_four() + { + $this->assertEquals(4, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithSwitchStatement.inc')); + } + + /** @test */ + public function this_class_with_many_methods_and_many_branches_should_have_a_complexity_of_eleven() + { + $this->assertEquals(11, $this->assess('tests/Unit/Assessors/CyclomaticComplexity/Assets/ClassWithManyMethodsAndLotsOfBranches.inc')); + } + + + protected function assess($filename) + { + return (new CyclomaticComplexityAssessor)->assess($filename); + } + + public function setup() + { + + } +} \ No newline at end of file