Permalink
Browse files

first draft of NPath complexity check

  • Loading branch information...
1 parent c1428cf commit 1bda0fd002195cca1fdc91f91c5206c05e582f43 @martinjandrews martinjandrews committed Aug 23, 2009
View
@@ -13,4 +13,5 @@
require 'roodi/checks/method_name_check'
require 'roodi/checks/module_line_count_check'
require 'roodi/checks/module_name_check'
+require 'roodi/checks/npath_complexity_method_check'
require 'roodi/checks/parameter_number_check'
@@ -0,0 +1,65 @@
+require 'roodi/checks/check'
+
+module Roodi
+ module Checks
+ class NpathComplexityCheck < Check
+ # , :when, :and, :or
+ MULTIPLYING_NODE_TYPES = [:if, :while, :until, :for, :case]
+ ADDING_NODE_TYPES = [:rescue]
+ COMPLEXITY_NODE_TYPES = MULTIPLYING_NODE_TYPES + ADDING_NODE_TYPES
+
+ def initialize(complexity)
+ super()
+ @complexity = complexity
+ @value_stack = []
+ @current_value = 1
+ end
+
+ COMPLEXITY_NODE_TYPES.each do |type|
+ define_method "evaluate_start_#{type}" do
+ @value_stack.push @current_value
+ @current_value = 1
+ end
+ end
+
+ MULTIPLYING_NODE_TYPES.each do |type|
+ define_method "evaluate_end_#{type}" do
+ pop = @value_stack.pop
+ @current_value = (@current_value + 1) * pop
+ end
+ end
+
+ ADDING_NODE_TYPES.each do |type|
+ define_method "evaluate_end_#{type}" do
+ pop = @value_stack.pop
+ @current_value = @current_value - 1 + pop
+ end
+ end
+
+ protected
+
+ def count_complexity(node)
+ count_branches(node) + 1
+ end
+
+ def increase_depth
+ @count = 1 unless counting?
+ @counting = @counting + 1
+ end
+
+ def decrease_depth
+ @counting = @counting - 1
+ if @counting <= 0
+ @counting = 0
+ evaluate_matching_end
+ end
+ end
+
+ private
+
+ def counting?
+ @counting > 0
+ end
+ end
+ end
+end
@@ -0,0 +1,31 @@
+require 'roodi/checks/npath_complexity_check'
+
+module Roodi
+ module Checks
+ # Checks Npath complexity of a method against a specified limit.
+ class NpathComplexityMethodCheck < NpathComplexityCheck
+ DEFAULT_COMPLEXITY = 8
+
+ def initialize(options = {})
+ complexity = options['complexity'] || DEFAULT_COMPLEXITY
+ super(complexity)
+ end
+
+ def interesting_nodes
+ [:defn] + COMPLEXITY_NODE_TYPES
+ end
+
+ def evaluate_start_defn(node)
+ @method_name = @node[1]
+ end
+
+ def evaluate_end_defn(node)
+ add_error "Method name \"#{@method_name}\" n-path complexity is #{@current_value}. It should be #{@complexity} or less." unless @current_value <= @complexity
+ end
+
+ def evaluate_matching_end
+ add_error "Method name \"#{@method_name}\" n-path complexity is #{@count}. It should be #{@complexity} or less." unless @count <= @complexity
+ end
+ end
+ end
+end
@@ -0,0 +1,45 @@
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
+
+describe Roodi::Checks::NpathComplexityMethodCheck do
+ before(:each) do
+ @roodi = Roodi::Core::Runner.new(Roodi::Checks::NpathComplexityMethodCheck.new({'complexity' => 0}))
+ end
+
+ def verify_content_complexity(content, complexity)
+ @roodi.check_content(content)
+ errors = @roodi.errors
+ errors.should_not be_empty
+ errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name\" n-path complexity is #{complexity}. It should be 0 or less.")
+ end
+
+ it "should default to 1" do
+ content = <<-END
+ def method_name
+ end
+ END
+ verify_content_complexity(content, 1)
+ end
+
+ it "should find an if block" do
+ content = <<-END
+ def method_name
+ call_foo if some_condition
+ end
+ END
+ verify_content_complexity(content, 2)
+ end
+
+ it "should find nested if block" do
+ content = <<-END
+ def method_name
+ if (value1 == 0)
+ if (value2 == 0 && value3 == 0)
+ end
+ if (value4 == 0 && value5 == 0)
+ end
+ end
+ end
+ END
+ verify_content_complexity(content, 5)
+ end
+end

0 comments on commit 1bda0fd

Please sign in to comment.