roodi 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|