roodi 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/Manifest.txt +3 -1
- data/Rakefile +2 -2
- data/lib/roodi.rb +1 -1
- data/lib/roodi/checks.rb +1 -0
- data/lib/roodi/checks/abc_metric_method_check.rb +1 -1
- data/lib/roodi/checks/assignment_in_conditional_check.rb +4 -2
- data/lib/roodi/checks/case_missing_else_check.rb +1 -1
- data/lib/roodi/checks/check.rb +28 -4
- data/lib/roodi/checks/class_variable_check.rb +1 -1
- data/lib/roodi/checks/control_coupling_check.rb +2 -2
- data/lib/roodi/checks/cyclomatic_complexity_block_check.rb +11 -4
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +24 -6
- data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +12 -4
- data/lib/roodi/checks/empty_rescue_body_check.rb +7 -8
- data/lib/roodi/checks/for_loop_check.rb +1 -1
- data/lib/roodi/checks/line_count_check.rb +1 -1
- data/lib/roodi/checks/name_check.rb +1 -1
- data/lib/roodi/checks/npath_complexity_check.rb +73 -0
- data/lib/roodi/checks/npath_complexity_method_check.rb +28 -0
- data/lib/roodi/checks/parameter_number_check.rb +1 -1
- data/lib/roodi/core/checking_visitor.rb +7 -4
- data/lib/roodi/core/parser.rb +9 -2
- data/lib/roodi/core/runner.rb +3 -3
- data/lib/roodi/core/visitable_sexp.rb +6 -1
- data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +41 -0
- data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +31 -0
- data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +17 -0
- data/spec/roodi/checks/npath_complexity_method_check_spec.rb +53 -0
- metadata +5 -13
- data/lib/roodi/core/iterator_visitor.rb +0 -19
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 2.0.0
|
2
|
+
|
3
|
+
* Changed internal structure to use a more pure visitor like pattern.
|
4
|
+
* Got *much* faster as a result of the change.
|
5
|
+
* Design change fixed 'feature' where nested blocks would all get listed if the inner one exceeded complexity.
|
6
|
+
* Outline for NPath complexity check is now possible. Not working yet though.
|
7
|
+
* Removed dependency on facets library.
|
8
|
+
|
1
9
|
= 1.4.0
|
2
10
|
|
3
11
|
* Upgraded from ParseTree to ruby_parser.
|
data/Manifest.txt
CHANGED
@@ -25,11 +25,12 @@ lib/roodi/checks/method_name_check.rb
|
|
25
25
|
lib/roodi/checks/module_line_count_check.rb
|
26
26
|
lib/roodi/checks/module_name_check.rb
|
27
27
|
lib/roodi/checks/name_check.rb
|
28
|
+
lib/roodi/checks/npath_complexity_check.rb
|
29
|
+
lib/roodi/checks/npath_complexity_method_check.rb
|
28
30
|
lib/roodi/checks/parameter_number_check.rb
|
29
31
|
lib/roodi/core.rb
|
30
32
|
lib/roodi/core/checking_visitor.rb
|
31
33
|
lib/roodi/core/error.rb
|
32
|
-
lib/roodi/core/iterator_visitor.rb
|
33
34
|
lib/roodi/core/parser.rb
|
34
35
|
lib/roodi/core/runner.rb
|
35
36
|
lib/roodi/core/visitable_sexp.rb
|
@@ -50,5 +51,6 @@ spec/roodi/checks/method_line_count_check_spec.rb
|
|
50
51
|
spec/roodi/checks/method_name_check_spec.rb
|
51
52
|
spec/roodi/checks/module_line_count_check_spec.rb
|
52
53
|
spec/roodi/checks/module_name_check_spec.rb
|
54
|
+
spec/roodi/checks/npath_complexity_method_check_spec.rb
|
53
55
|
spec/roodi/checks/parameter_number_check_spec.rb
|
54
56
|
spec/spec_helper.rb
|
data/Rakefile
CHANGED
@@ -8,12 +8,12 @@ require 'roodi'
|
|
8
8
|
|
9
9
|
Hoe.new('roodi', Roodi::VERSION) do |p|
|
10
10
|
p.developer('Marty Andrews', 'marty@cogentconsulting.com.au')
|
11
|
-
p.extra_deps = ['ruby_parser'
|
11
|
+
p.extra_deps = ['ruby_parser']
|
12
12
|
p.remote_rdoc_dir = ''
|
13
13
|
end
|
14
14
|
|
15
15
|
def roodi(ruby_files)
|
16
|
-
roodi = Roodi::Core::
|
16
|
+
roodi = Roodi::Core::Runner.new
|
17
17
|
ruby_files.each { |file| roodi.check_file(file) }
|
18
18
|
roodi.errors.each {|error| puts error}
|
19
19
|
puts "\nFound #{roodi.errors.size} errors."
|
data/lib/roodi.rb
CHANGED
data/lib/roodi/checks.rb
CHANGED
@@ -13,4 +13,5 @@ require 'roodi/checks/method_line_count_check'
|
|
13
13
|
require 'roodi/checks/method_name_check'
|
14
14
|
require 'roodi/checks/module_line_count_check'
|
15
15
|
require 'roodi/checks/module_name_check'
|
16
|
+
require 'roodi/checks/npath_complexity_method_check'
|
16
17
|
require 'roodi/checks/parameter_number_check'
|
@@ -15,7 +15,7 @@ module Roodi
|
|
15
15
|
[:if, :while]
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def evaluate_start(node)
|
19
19
|
add_error("Found = in conditional. It should probably be an ==") if has_assignment?(node[1])
|
20
20
|
end
|
21
21
|
|
@@ -24,7 +24,9 @@ module Roodi
|
|
24
24
|
def has_assignment?(node)
|
25
25
|
found_assignment = false
|
26
26
|
found_assignment = found_assignment || node.node_type == :lasgn
|
27
|
-
node.
|
27
|
+
if (node.node_type == :and or node.node_type == :or)
|
28
|
+
node.children.each { |child| found_assignment = found_assignment || has_assignment?(child) }
|
29
|
+
end
|
28
30
|
found_assignment
|
29
31
|
end
|
30
32
|
end
|
data/lib/roodi/checks/check.rb
CHANGED
@@ -3,19 +3,43 @@ require 'roodi/core/error'
|
|
3
3
|
module Roodi
|
4
4
|
module Checks
|
5
5
|
class Check
|
6
|
+
NODE_TYPES = [:defn, :module, :resbody, :lvar, :cvar, :class, :if, :while, :until, :for, :rescue, :case, :when, :and, :or]
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
@errors = []
|
8
10
|
end
|
9
11
|
|
12
|
+
NODE_TYPES.each do |node|
|
13
|
+
start_node_method = "evaluate_start_#{node}"
|
14
|
+
end_node_method = "evaluate_end_#{node}"
|
15
|
+
define_method(start_node_method) { } unless self.respond_to?(start_node_method)
|
16
|
+
define_method(end_node_method) { } unless self.respond_to?(end_node_method)
|
17
|
+
end
|
18
|
+
|
10
19
|
def position(offset = 0)
|
11
20
|
"#{@line[2]}:#{@line[1] + offset}"
|
12
21
|
end
|
22
|
+
|
23
|
+
def evaluate_start(node)
|
24
|
+
end
|
25
|
+
|
26
|
+
def evaluate_end(node)
|
27
|
+
end
|
13
28
|
|
14
|
-
def evaluate_node(node)
|
29
|
+
def evaluate_node(position, node)
|
15
30
|
@node = node
|
16
|
-
eval_method = "evaluate_#{node.node_type}"
|
17
|
-
self.send(eval_method, node)
|
18
|
-
|
31
|
+
eval_method = "evaluate_#{position}_#{node.node_type}"
|
32
|
+
self.send(eval_method, node)
|
33
|
+
end
|
34
|
+
|
35
|
+
def evaluate_node_start(node)
|
36
|
+
evaluate_node(:start, node)
|
37
|
+
evaluate_start(node)
|
38
|
+
end
|
39
|
+
|
40
|
+
def evaluate_node_end(node)
|
41
|
+
evaluate_node(:end, node)
|
42
|
+
evaluate_end(node)
|
19
43
|
end
|
20
44
|
|
21
45
|
def add_error(error)
|
@@ -7,12 +7,12 @@ module Roodi
|
|
7
7
|
[:defn, :lvar]
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
10
|
+
def evaluate_start_defn(node)
|
11
11
|
@method_name = node[1]
|
12
12
|
@arguments = node[2][1..-1]
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def evaluate_start_lvar(node)
|
16
16
|
add_error "Method \"#{@method_name}\" uses the argument \"#{node[1]}\" for internal control." if @arguments.detect {|each| each == node[1]}
|
17
17
|
end
|
18
18
|
end
|
@@ -20,12 +20,19 @@ module Roodi
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def interesting_nodes
|
23
|
-
[:iter]
|
23
|
+
[:iter] + COMPLEXITY_NODE_TYPES
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
26
|
+
def evaluate_start_iter(node)
|
27
|
+
increase_depth
|
28
|
+
end
|
29
|
+
|
30
|
+
def evaluate_end_iter(node)
|
31
|
+
decrease_depth
|
32
|
+
end
|
33
|
+
|
34
|
+
def evaluate_matching_end
|
35
|
+
add_error "Block cyclomatic complexity is #{@count}. It should be #{@complexity} or less." unless @count <= @complexity
|
29
36
|
end
|
30
37
|
end
|
31
38
|
end
|
@@ -8,6 +8,14 @@ module Roodi
|
|
8
8
|
def initialize(complexity)
|
9
9
|
super()
|
10
10
|
@complexity = complexity
|
11
|
+
@count = 0
|
12
|
+
@counting = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
COMPLEXITY_NODE_TYPES.each do |type|
|
16
|
+
define_method "evaluate_start_#{type}" do
|
17
|
+
@count = @count + 1 if counting?
|
18
|
+
end
|
11
19
|
end
|
12
20
|
|
13
21
|
protected
|
@@ -15,14 +23,24 @@ module Roodi
|
|
15
23
|
def count_complexity(node)
|
16
24
|
count_branches(node) + 1
|
17
25
|
end
|
26
|
+
|
27
|
+
def increase_depth
|
28
|
+
@count = 1 unless counting?
|
29
|
+
@counting = @counting + 1
|
30
|
+
end
|
18
31
|
|
32
|
+
def decrease_depth
|
33
|
+
@counting = @counting - 1
|
34
|
+
if @counting <= 0
|
35
|
+
@counting = 0
|
36
|
+
evaluate_matching_end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
19
40
|
private
|
20
|
-
|
21
|
-
def
|
22
|
-
|
23
|
-
count = count + 1 if COMPLEXITY_NODE_TYPES.include? node.node_type
|
24
|
-
node.children.each {|node| count += count_branches(node)}
|
25
|
-
count
|
41
|
+
|
42
|
+
def counting?
|
43
|
+
@counting > 0
|
26
44
|
end
|
27
45
|
end
|
28
46
|
end
|
@@ -20,12 +20,20 @@ module Roodi
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def interesting_nodes
|
23
|
-
[:defn]
|
23
|
+
[:defn] + COMPLEXITY_NODE_TYPES
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
26
|
+
def evaluate_start_defn(node)
|
27
|
+
@method_name = @node[1]
|
28
|
+
increase_depth
|
29
|
+
end
|
30
|
+
|
31
|
+
def evaluate_end_defn(node)
|
32
|
+
decrease_depth
|
33
|
+
end
|
34
|
+
|
35
|
+
def evaluate_matching_end
|
36
|
+
add_error "Method name \"#{@method_name}\" cyclomatic complexity is #{@count}. It should be #{@complexity} or less." unless @count <= @complexity
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
@@ -7,26 +7,25 @@ module Roodi
|
|
7
7
|
# When the body of a rescue block is empty, exceptions can get caught and swallowed without
|
8
8
|
# any feedback to the user.
|
9
9
|
class EmptyRescueBodyCheck < Check
|
10
|
-
STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :nil, :call]
|
10
|
+
STATEMENT_NODES = [:fcall, :return, :attrasgn, :vcall, :nil, :call, :lasgn]
|
11
11
|
|
12
12
|
def interesting_nodes
|
13
13
|
[:resbody]
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
add_error("Rescue block should not be empty.") unless has_statement?(node)
|
16
|
+
def evaluate_start(node)
|
17
|
+
add_error("Rescue block should not be empty.") unless has_statement?(node.children[1])
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def has_statement?(node)
|
23
|
-
|
24
|
-
|
25
|
-
return true if node.children.any? { |child| has_statement?(child) }
|
23
|
+
false unless node
|
24
|
+
has_local_statement?(node) or node.children.any? { |child| has_statement?(child) } if node
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
29
|
-
node.node_type
|
27
|
+
def has_local_statement?(node)
|
28
|
+
STATEMENT_NODES.include?(node.node_type)
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
@@ -14,7 +14,7 @@ module Roodi
|
|
14
14
|
@interesting_nodes
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def evaluate_start(node)
|
18
18
|
line_count = count_lines(node)
|
19
19
|
add_error "#{@message_prefix} \"#{node[1]}\" has #{line_count} lines. It should have #{@line_count} or less." unless line_count <= @line_count
|
20
20
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'roodi/checks/check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
class NpathComplexityCheck < Check
|
6
|
+
# , :when, :and, :or
|
7
|
+
MULTIPLYING_NODE_TYPES = [:if, :while, :until, :for, :case]
|
8
|
+
ADDING_NODE_TYPES = [:rescue]
|
9
|
+
COMPLEXITY_NODE_TYPES = MULTIPLYING_NODE_TYPES + ADDING_NODE_TYPES
|
10
|
+
|
11
|
+
def initialize(complexity)
|
12
|
+
super()
|
13
|
+
@complexity = complexity
|
14
|
+
@value_stack = []
|
15
|
+
@current_value = 1
|
16
|
+
end
|
17
|
+
|
18
|
+
def evalute_start_if(node)
|
19
|
+
push_value
|
20
|
+
end
|
21
|
+
|
22
|
+
def evalute_start_while(node)
|
23
|
+
push_value
|
24
|
+
end
|
25
|
+
|
26
|
+
def evalute_start_until(node)
|
27
|
+
push_value
|
28
|
+
end
|
29
|
+
|
30
|
+
def evalute_start_for(node)
|
31
|
+
push_value
|
32
|
+
end
|
33
|
+
|
34
|
+
def evalute_start_case(node)
|
35
|
+
push_value
|
36
|
+
end
|
37
|
+
|
38
|
+
def evalute_start_rescue(node)
|
39
|
+
push_value
|
40
|
+
end
|
41
|
+
|
42
|
+
MULTIPLYING_NODE_TYPES.each do |type|
|
43
|
+
define_method "evaluate_end_#{type}" do |node|
|
44
|
+
leave_multiplying_conditional
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
ADDING_NODE_TYPES.each do |type|
|
49
|
+
define_method "evaluate_end_#{type}" do |node|
|
50
|
+
leave_multiplying_conditional
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def push_value
|
57
|
+
@value_stack.push @current_value
|
58
|
+
@current_value = 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def leave_multiplying_conditional
|
62
|
+
pop = @value_stack.pop
|
63
|
+
@current_value = (@current_value + 1) * pop
|
64
|
+
end
|
65
|
+
|
66
|
+
def leave_adding_conditional
|
67
|
+
pop = @value_stack.pop
|
68
|
+
puts "#{type}, so adding #{pop}"
|
69
|
+
@current_value = @current_value - 1 + pop
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'roodi/checks/npath_complexity_check'
|
2
|
+
|
3
|
+
module Roodi
|
4
|
+
module Checks
|
5
|
+
# Checks Npath complexity of a method against a specified limit.
|
6
|
+
class NpathComplexityMethodCheck < NpathComplexityCheck
|
7
|
+
DEFAULT_COMPLEXITY = 8
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
complexity = options['complexity'] || DEFAULT_COMPLEXITY
|
11
|
+
super(complexity)
|
12
|
+
end
|
13
|
+
|
14
|
+
def interesting_nodes
|
15
|
+
[:defn] + COMPLEXITY_NODE_TYPES
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate_start_defn(node)
|
19
|
+
@method_name = @node[1]
|
20
|
+
push_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def evaluate_end_defn(node)
|
24
|
+
add_error "Method name \"#{@method_name}\" n-path complexity is #{@current_value}. It should be #{@complexity} or less." unless @current_value <= @complexity
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -13,11 +13,14 @@ module Roodi
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
def visit(node)
|
17
17
|
checks = @checks[node.node_type]
|
18
|
-
checks.each {|check| check.
|
19
|
-
|
20
|
-
|
18
|
+
checks.each {|check| check.evaluate_node_start(node)} unless checks.nil?
|
19
|
+
|
20
|
+
node.visitable_children.each {|sexp| sexp.accept(self)}
|
21
|
+
|
22
|
+
checks.each {|check| check.evaluate_node_end(node)} unless checks.nil?
|
23
|
+
end
|
21
24
|
end
|
22
25
|
end
|
23
26
|
end
|
data/lib/roodi/core/parser.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'ruby_parser'
|
3
|
-
require 'facets'
|
4
|
-
|
5
3
|
|
6
4
|
module Roodi
|
7
5
|
module Core
|
@@ -14,6 +12,15 @@ module Roodi
|
|
14
12
|
|
15
13
|
private
|
16
14
|
|
15
|
+
def silence_stream(stream)
|
16
|
+
old_stream = stream.dup
|
17
|
+
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
|
18
|
+
stream.sync = true
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
stream.reopen(old_stream)
|
22
|
+
end
|
23
|
+
|
17
24
|
def silent_parse(content, filename)
|
18
25
|
@parser ||= RubyParser.new
|
19
26
|
@parser.parse(content, filename)
|
data/lib/roodi/core/runner.rb
CHANGED
@@ -2,7 +2,6 @@ require 'pp'
|
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
require 'roodi/core/checking_visitor'
|
5
|
-
require 'roodi/core/iterator_visitor'
|
6
5
|
require 'roodi/core/parser'
|
7
6
|
require 'roodi/core/visitable_sexp'
|
8
7
|
|
@@ -16,13 +15,14 @@ module Roodi
|
|
16
15
|
def initialize(*checks)
|
17
16
|
@config = DEFAULT_CONFIG
|
18
17
|
@checks = checks unless checks.empty?
|
18
|
+
@checks ||= load_checks
|
19
|
+
@checker ||= CheckingVisitor.new(@checks)
|
19
20
|
@parser = Parser.new
|
20
21
|
end
|
21
22
|
|
22
23
|
def check(filename, content)
|
23
|
-
@checks ||= load_checks
|
24
24
|
node = parse(filename, content)
|
25
|
-
node.accept(
|
25
|
+
node.accept(@checker) if node
|
26
26
|
end
|
27
27
|
|
28
28
|
def check_content(content)
|
@@ -13,8 +13,13 @@ class Sexp
|
|
13
13
|
def children
|
14
14
|
find_all { | sexp | Sexp === sexp }
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def is_language_node?
|
18
18
|
first.class == Symbol
|
19
19
|
end
|
20
|
+
|
21
|
+
def visitable_children
|
22
|
+
parent = is_language_node? ? sexp_body : self
|
23
|
+
parent.children
|
24
|
+
end
|
20
25
|
end
|
@@ -61,4 +61,45 @@ describe Roodi::Checks::AssignmentInConditionalCheck do
|
|
61
61
|
errors.should_not be_empty
|
62
62
|
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
63
63
|
end
|
64
|
+
|
65
|
+
it "should reject an assignment after an 'and'" do
|
66
|
+
content = <<-END
|
67
|
+
call_foo if bar and bam = baz
|
68
|
+
END
|
69
|
+
@roodi.check_content(content)
|
70
|
+
errors = @roodi.errors
|
71
|
+
errors.should_not be_empty
|
72
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
it "should reject an assignment after an 'or'" do
|
77
|
+
content = <<-END
|
78
|
+
call_foo if bar or bam = baz
|
79
|
+
END
|
80
|
+
@roodi.check_content(content)
|
81
|
+
errors = @roodi.errors
|
82
|
+
errors.should_not be_empty
|
83
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should reject an assignment after an '&&'" do
|
87
|
+
content = <<-END
|
88
|
+
call_foo if bar && bam = baz
|
89
|
+
END
|
90
|
+
@roodi.check_content(content)
|
91
|
+
errors = @roodi.errors
|
92
|
+
errors.should_not be_empty
|
93
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should reject an assignment after an '||'" do
|
97
|
+
content = <<-END
|
98
|
+
call_foo if bar || bam = baz
|
99
|
+
END
|
100
|
+
@roodi.check_content(content)
|
101
|
+
errors = @roodi.errors
|
102
|
+
errors.should_not be_empty
|
103
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
104
|
+
end
|
64
105
|
end
|
@@ -33,4 +33,35 @@ describe Roodi::Checks::CyclomaticComplexityBlockCheck do
|
|
33
33
|
END
|
34
34
|
verify_content_complexity(content, 2)
|
35
35
|
end
|
36
|
+
|
37
|
+
it "should evaluate real example 1 correctly" do
|
38
|
+
content = <<-END
|
39
|
+
def method_name
|
40
|
+
UNIXMbox.lock(@filename) {|f|
|
41
|
+
begin
|
42
|
+
f.each do |line|
|
43
|
+
if /\AFrom / === line
|
44
|
+
w.close if w
|
45
|
+
File.utime time, time, port.filename if time
|
46
|
+
|
47
|
+
port = @real.new_port
|
48
|
+
w = port.wopen
|
49
|
+
time = fromline2time(line)
|
50
|
+
else
|
51
|
+
w.print line if w
|
52
|
+
end
|
53
|
+
end
|
54
|
+
ensure
|
55
|
+
if w and not w.closed?
|
56
|
+
w.close
|
57
|
+
File.utime time, time, port.filename if time
|
58
|
+
end
|
59
|
+
end
|
60
|
+
f.truncate(0) unless @readonly
|
61
|
+
@updated = Time.now
|
62
|
+
}
|
63
|
+
end
|
64
|
+
END
|
65
|
+
verify_content_complexity(content, 9)
|
66
|
+
end
|
36
67
|
end
|
@@ -180,4 +180,21 @@ describe Roodi::Checks::CyclomaticComplexityMethodCheck do
|
|
180
180
|
verify_content_complexity(content, 5)
|
181
181
|
end
|
182
182
|
|
183
|
+
it "should count only a single method" do
|
184
|
+
content = <<-END
|
185
|
+
def method_name_1
|
186
|
+
call_foo if some_condition
|
187
|
+
end
|
188
|
+
def method_name_2
|
189
|
+
call_foo if some_condition
|
190
|
+
end
|
191
|
+
END
|
192
|
+
|
193
|
+
@roodi.check_content(content)
|
194
|
+
errors = @roodi.errors
|
195
|
+
errors.should_not be_empty
|
196
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name_1\" cyclomatic complexity is 2. It should be 0 or less.")
|
197
|
+
errors[1].to_s.should eql("dummy-file.rb:4 - Method name \"method_name_2\" cyclomatic complexity is 2. It should be 0 or less.")
|
198
|
+
end
|
199
|
+
|
183
200
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::NpathComplexityMethodCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::NpathComplexityMethodCheck.new({'complexity' => 0}))
|
6
|
+
end
|
7
|
+
|
8
|
+
def verify_content_complexity(content, complexity)
|
9
|
+
@roodi.check_content(content)
|
10
|
+
errors = @roodi.errors
|
11
|
+
errors.should_not be_empty
|
12
|
+
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.")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should default to 1" do
|
16
|
+
content = <<-END
|
17
|
+
def method_name
|
18
|
+
end
|
19
|
+
END
|
20
|
+
verify_content_complexity(content, 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should find an if block" do
|
24
|
+
content = <<-END
|
25
|
+
def method_name
|
26
|
+
call_foo if some_condition
|
27
|
+
end
|
28
|
+
END
|
29
|
+
verify_content_complexity(content, 2)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should find nested if block" do
|
33
|
+
pending "NPath Complexity implementation that can support 'else' blocks"
|
34
|
+
content = <<-END
|
35
|
+
def method_name
|
36
|
+
if (value1)
|
37
|
+
foo
|
38
|
+
else
|
39
|
+
bar
|
40
|
+
end
|
41
|
+
if (value2)
|
42
|
+
bam
|
43
|
+
else
|
44
|
+
baz
|
45
|
+
end
|
46
|
+
if (value3)
|
47
|
+
one
|
48
|
+
end
|
49
|
+
end
|
50
|
+
END
|
51
|
+
verify_content_complexity(content, 8)
|
52
|
+
end
|
53
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roodi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marty Andrews
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-23 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,16 +22,6 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: "0"
|
24
24
|
version:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: facets
|
27
|
-
type: :runtime
|
28
|
-
version_requirement:
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: "0"
|
34
|
-
version:
|
35
25
|
- !ruby/object:Gem::Dependency
|
36
26
|
name: hoe
|
37
27
|
type: :development
|
@@ -82,11 +72,12 @@ files:
|
|
82
72
|
- lib/roodi/checks/module_line_count_check.rb
|
83
73
|
- lib/roodi/checks/module_name_check.rb
|
84
74
|
- lib/roodi/checks/name_check.rb
|
75
|
+
- lib/roodi/checks/npath_complexity_check.rb
|
76
|
+
- lib/roodi/checks/npath_complexity_method_check.rb
|
85
77
|
- lib/roodi/checks/parameter_number_check.rb
|
86
78
|
- lib/roodi/core.rb
|
87
79
|
- lib/roodi/core/checking_visitor.rb
|
88
80
|
- lib/roodi/core/error.rb
|
89
|
-
- lib/roodi/core/iterator_visitor.rb
|
90
81
|
- lib/roodi/core/parser.rb
|
91
82
|
- lib/roodi/core/runner.rb
|
92
83
|
- lib/roodi/core/visitable_sexp.rb
|
@@ -107,6 +98,7 @@ files:
|
|
107
98
|
- spec/roodi/checks/method_name_check_spec.rb
|
108
99
|
- spec/roodi/checks/module_line_count_check_spec.rb
|
109
100
|
- spec/roodi/checks/module_name_check_spec.rb
|
101
|
+
- spec/roodi/checks/npath_complexity_method_check_spec.rb
|
110
102
|
- spec/roodi/checks/parameter_number_check_spec.rb
|
111
103
|
- spec/spec_helper.rb
|
112
104
|
has_rdoc: true
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Roodi
|
2
|
-
module Core
|
3
|
-
class IteratorVisitor
|
4
|
-
def initialize(payload)
|
5
|
-
@payload = payload
|
6
|
-
end
|
7
|
-
|
8
|
-
def visit(visited)
|
9
|
-
visited.accept(@payload)
|
10
|
-
visitable_nodes = visited.is_language_node? ? visited.sexp_body : visited
|
11
|
-
visitable_nodes.each do |child|
|
12
|
-
if child.class == Sexp then
|
13
|
-
child.accept(self)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|