roodi1.9 2.0.1
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/.gitignore +2 -0
- data/Gemfile +0 -0
- data/History.txt +88 -0
- data/Manifest.txt +56 -0
- data/README.md +123 -0
- data/Rakefile +40 -0
- data/bin/roodi1.9 +21 -0
- data/bin/roodi1.9-describe +7 -0
- data/lib/roodi/checks/abc_metric_method_check.rb +77 -0
- data/lib/roodi/checks/assignment_in_conditional_check.rb +34 -0
- data/lib/roodi/checks/case_missing_else_check.rb +20 -0
- data/lib/roodi/checks/check.rb +61 -0
- data/lib/roodi/checks/class_line_count_check.rb +18 -0
- data/lib/roodi/checks/class_name_check.rb +21 -0
- data/lib/roodi/checks/class_variable_check.rb +24 -0
- data/lib/roodi/checks/control_coupling_check.rb +20 -0
- data/lib/roodi/checks/cyclomatic_complexity_block_check.rb +39 -0
- data/lib/roodi/checks/cyclomatic_complexity_check.rb +47 -0
- data/lib/roodi/checks/cyclomatic_complexity_method_check.rb +40 -0
- data/lib/roodi/checks/empty_rescue_body_check.rb +32 -0
- data/lib/roodi/checks/for_loop_check.rb +20 -0
- data/lib/roodi/checks/line_count_check.rb +29 -0
- data/lib/roodi/checks/method_line_count_check.rb +19 -0
- data/lib/roodi/checks/method_name_check.rb +21 -0
- data/lib/roodi/checks/missing_foreign_key_index_check.rb +98 -0
- data/lib/roodi/checks/module_line_count_check.rb +18 -0
- data/lib/roodi/checks/module_name_check.rb +21 -0
- data/lib/roodi/checks/name_check.rb +23 -0
- 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 +30 -0
- data/lib/roodi/checks.rb +18 -0
- data/lib/roodi/core/checking_visitor.rb +26 -0
- data/lib/roodi/core/error.rb +17 -0
- data/lib/roodi/core/parser.rb +30 -0
- data/lib/roodi/core/runner.rb +80 -0
- data/lib/roodi/core/visitable_sexp.rb +25 -0
- data/lib/roodi/core.rb +1 -0
- data/lib/roodi.rb +6 -0
- data/lib/roodi_task.rb +35 -0
- data/roodi.yml +19 -0
- data/roodi1.9.gemspec +17 -0
- data/spec/roodi/checks/abc_metric_method_check_spec.rb +89 -0
- data/spec/roodi/checks/assignment_in_conditional_check_spec.rb +105 -0
- data/spec/roodi/checks/case_missing_else_check_spec.rb +32 -0
- data/spec/roodi/checks/class_line_count_check_spec.rb +39 -0
- data/spec/roodi/checks/class_name_check_spec.rb +39 -0
- data/spec/roodi/checks/class_variable_check_spec.rb +17 -0
- data/spec/roodi/checks/control_coupling_check_spec.rb +23 -0
- data/spec/roodi/checks/cyclomatic_complexity_block_check_spec.rb +67 -0
- data/spec/roodi/checks/cyclomatic_complexity_method_check_spec.rb +200 -0
- data/spec/roodi/checks/empty_rescue_body_check_spec.rb +140 -0
- data/spec/roodi/checks/for_loop_check_spec.rb +18 -0
- data/spec/roodi/checks/method_line_count_check_spec.rb +56 -0
- data/spec/roodi/checks/method_name_check_spec.rb +76 -0
- data/spec/roodi/checks/missing_foreign_key_index_check_spec.rb +33 -0
- data/spec/roodi/checks/module_line_count_check_spec.rb +39 -0
- data/spec/roodi/checks/module_name_check_spec.rb +27 -0
- data/spec/roodi/checks/npath_complexity_method_check_spec.rb +53 -0
- data/spec/roodi/checks/parameter_number_check_spec.rb +47 -0
- data/spec/roodi/core/runner_spec.rb +25 -0
- data/spec/roodi/roodi.yml +1 -0
- data/spec/spec_helper.rb +3 -0
- metadata +112 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sexp'
|
3
|
+
|
4
|
+
class Sexp
|
5
|
+
def accept(visitor)
|
6
|
+
visitor.visit(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def node_type
|
10
|
+
first
|
11
|
+
end
|
12
|
+
|
13
|
+
def children
|
14
|
+
find_all { | sexp | Sexp === sexp }
|
15
|
+
end
|
16
|
+
|
17
|
+
def is_language_node?
|
18
|
+
first.class == Symbol
|
19
|
+
end
|
20
|
+
|
21
|
+
def visitable_children
|
22
|
+
parent = is_language_node? ? sexp_body : self
|
23
|
+
parent.children
|
24
|
+
end
|
25
|
+
end
|
data/lib/roodi/core.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'roodi/core/runner'
|
data/lib/roodi.rb
ADDED
data/lib/roodi_task.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
class RoodiTask < Rake::TaskLib
|
2
|
+
attr_accessor :name
|
3
|
+
attr_accessor :patterns
|
4
|
+
attr_accessor :config
|
5
|
+
attr_accessor :verbose
|
6
|
+
|
7
|
+
def initialize name = :roodi, patterns = nil, config = nil
|
8
|
+
@name = name
|
9
|
+
@patterns = patterns || %w(app/**/*.rb lib/**/*.rb spec/**/*.rb test/**/*.rb)
|
10
|
+
@config = config
|
11
|
+
@verbose = Rake.application.options.trace
|
12
|
+
|
13
|
+
yield self if block_given?
|
14
|
+
|
15
|
+
define
|
16
|
+
end
|
17
|
+
|
18
|
+
def define
|
19
|
+
desc "Check for design issues in: #{patterns.join(', ')}"
|
20
|
+
task name do
|
21
|
+
runner = Roodi::Core::Runner.new
|
22
|
+
|
23
|
+
runner.config = config if config
|
24
|
+
|
25
|
+
patterns.each do |pattern|
|
26
|
+
Dir.glob(pattern).each { |file| runner.check_file(file) }
|
27
|
+
end
|
28
|
+
|
29
|
+
runner.errors.each {|error| puts error}
|
30
|
+
|
31
|
+
raise "Found #{runner.errors.size} errors." unless runner.errors.empty?
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
end
|
data/roodi.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
AssignmentInConditionalCheck: { }
|
2
|
+
CaseMissingElseCheck: { }
|
3
|
+
ClassLineCountCheck: { line_count: 300 }
|
4
|
+
ClassNameCheck:
|
5
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
6
|
+
ClassVariableCheck: { }
|
7
|
+
CyclomaticComplexityBlockCheck: { complexity: 4 }
|
8
|
+
CyclomaticComplexityMethodCheck: { complexity: 8 }
|
9
|
+
EmptyRescueBodyCheck: { }
|
10
|
+
ForLoopCheck: { }
|
11
|
+
MethodLineCountCheck: { line_count: 20 }
|
12
|
+
MethodNameCheck:
|
13
|
+
pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
14
|
+
# MissingForeignKeyIndexCheck: { }
|
15
|
+
ModuleLineCountCheck: { line_count: 300 }
|
16
|
+
ModuleNameCheck:
|
17
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
18
|
+
ParameterNumberCheck: { parameter_count: 5 }
|
19
|
+
|
data/roodi1.9.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "roodi1.9"
|
5
|
+
s.version = "2.0.1"
|
6
|
+
s.date = "2012-07-19"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Marty Andrews"]
|
9
|
+
s.email = ["marty@martyandrews.net"]
|
10
|
+
s.homepage = "http://github.com/grsmv/roodi1.9"
|
11
|
+
s.summary = "Fork of Ruby Object Oriented Design Inferometer for Ruby 1.9"
|
12
|
+
s.description = "Roodi stands for Ruby Object Oriented Design Inferometer. It parses your Ruby code and warns you about design issues you have based on the checks that is has configured."
|
13
|
+
s.files = `git ls-files`.split($\).delete_if { |file| file = ~ /^\.\w/ }
|
14
|
+
s.bindir = 'bin'
|
15
|
+
s.executables << 'roodi1.9' << 'roodi1.9-describe'
|
16
|
+
s.rubyforge_project = "roodi"
|
17
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::AbcMetricMethodCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::AbcMetricMethodCheck.new({'score' => 0}))
|
6
|
+
end
|
7
|
+
|
8
|
+
def verify_content_score(content, a, b, c)
|
9
|
+
score = Math.sqrt(a*a + b*b + c*c)
|
10
|
+
@roodi.check_content(content)
|
11
|
+
errors = @roodi.errors
|
12
|
+
errors.should_not be_empty
|
13
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Method name \"method_name\" has an ABC metric score of <#{a},#{b},#{c}> = #{score}. It should be 0 or less.")
|
14
|
+
end
|
15
|
+
|
16
|
+
# 1. Add one to the assignment count for each occurrence of an assignment
|
17
|
+
# operator, excluding constant declarations:
|
18
|
+
#
|
19
|
+
# = *= /= %= += <<= >>= &= |= ^=
|
20
|
+
describe "when processing assignments" do
|
21
|
+
['=', '*=', '/=', '%=', '+=', '<<=', '>>=', '&=', '|=', '^='].each do |each|
|
22
|
+
it "should find #{each}" do
|
23
|
+
content = <<-END
|
24
|
+
def method_name
|
25
|
+
foo #{each} 1
|
26
|
+
end
|
27
|
+
END
|
28
|
+
verify_content_score(content, 1, 0, 0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# 3. Add one to the branch count for each function call or class method
|
34
|
+
# call.
|
35
|
+
#
|
36
|
+
# 4. Add one to the branch count for each occurrence of the new operator.
|
37
|
+
describe "when processing branches" do
|
38
|
+
it "should find a virtual method call" do
|
39
|
+
content = <<-END
|
40
|
+
def method_name
|
41
|
+
call_foo
|
42
|
+
end
|
43
|
+
END
|
44
|
+
verify_content_score(content, 0, 1, 0)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should find an explicit method call" do
|
48
|
+
content = <<-END
|
49
|
+
def method_name
|
50
|
+
@object.call_foo
|
51
|
+
end
|
52
|
+
END
|
53
|
+
verify_content_score(content, 0, 1, 0)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should exclude a condition" do
|
57
|
+
content = <<-END
|
58
|
+
def method_name
|
59
|
+
@object.call_foo < 10
|
60
|
+
end
|
61
|
+
END
|
62
|
+
verify_content_score(content, 0, 1, 1)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# 5. Add one to the condition count for each use of a conditional operator:
|
67
|
+
#
|
68
|
+
# == != <= >= < >
|
69
|
+
#
|
70
|
+
# 6. Add one to the condition count for each use of the following
|
71
|
+
# keywords:
|
72
|
+
#
|
73
|
+
# else case default try catch ?
|
74
|
+
#
|
75
|
+
# 7. Add one to the condition count for each unary conditional
|
76
|
+
# expression.
|
77
|
+
describe "when processing conditions" do
|
78
|
+
['==', '!=', '<=', '>=', '<', '>'].each do |each|
|
79
|
+
it "should find #{each}" do
|
80
|
+
content = <<-END
|
81
|
+
def method_name
|
82
|
+
2 #{each} 1
|
83
|
+
end
|
84
|
+
END
|
85
|
+
verify_content_score(content, 0, 0, 1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::AssignmentInConditionalCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::AssignmentInConditionalCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept an assignment before an if clause" do
|
9
|
+
content = <<-END
|
10
|
+
count = count + 1 if some_condition
|
11
|
+
END
|
12
|
+
@roodi.check_content(content)
|
13
|
+
errors = @roodi.errors
|
14
|
+
errors.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should reject an assignment inside an if clause" do
|
18
|
+
content = <<-END
|
19
|
+
call_foo if bar = bam
|
20
|
+
END
|
21
|
+
@roodi.check_content(content)
|
22
|
+
errors = @roodi.errors
|
23
|
+
errors.should_not be_empty
|
24
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should reject an assignment inside an unless clause" do
|
28
|
+
content = <<-END
|
29
|
+
call_foo unless bar = bam
|
30
|
+
END
|
31
|
+
@roodi.check_content(content)
|
32
|
+
errors = @roodi.errors
|
33
|
+
errors.should_not be_empty
|
34
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should reject an assignment inside a while clause" do
|
38
|
+
content = <<-END
|
39
|
+
call_foo while bar = bam
|
40
|
+
END
|
41
|
+
@roodi.check_content(content)
|
42
|
+
errors = @roodi.errors
|
43
|
+
errors.should_not be_empty
|
44
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should reject an assignment inside an unless clause" do
|
48
|
+
content = <<-END
|
49
|
+
call_foo while bar = bam
|
50
|
+
END
|
51
|
+
@roodi.check_content(content)
|
52
|
+
errors = @roodi.errors
|
53
|
+
errors.should_not be_empty
|
54
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should reject an assignment inside a a ternary operator check clause" do
|
58
|
+
content = 'call_foo (bar = bam) ? baz : bad'
|
59
|
+
@roodi.check_content(content)
|
60
|
+
errors = @roodi.errors
|
61
|
+
errors.should_not be_empty
|
62
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Found = in conditional. It should probably be an ==")
|
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
|
105
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::CaseMissingElseCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::CaseMissingElseCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept case statements that do have an else" do
|
9
|
+
content = <<-END
|
10
|
+
case foo
|
11
|
+
when "bar": "ok"
|
12
|
+
else "good"
|
13
|
+
end
|
14
|
+
END
|
15
|
+
@roodi.check_content(content)
|
16
|
+
errors = @roodi.errors
|
17
|
+
errors.should be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should reject case statements that do have an else" do
|
21
|
+
content = <<-END
|
22
|
+
case foo
|
23
|
+
when "bar": "ok"
|
24
|
+
when "bar": "bad"
|
25
|
+
end
|
26
|
+
END
|
27
|
+
@roodi.check_content(content)
|
28
|
+
errors = @roodi.errors
|
29
|
+
errors.should_not be_empty
|
30
|
+
errors[0].to_s.should match(/dummy-file.rb:[1-2] - Case statement is missing an else clause./)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ClassLineCountCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::ClassLineCountCheck.new({'line_count' => 1}))
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept classes with less lines than the threshold" do
|
9
|
+
content = <<-END
|
10
|
+
class ZeroLineClass
|
11
|
+
end
|
12
|
+
END
|
13
|
+
@roodi.check_content(content)
|
14
|
+
@roodi.errors.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should accept classes with the same number of lines as the threshold" do
|
18
|
+
content = <<-END
|
19
|
+
Class OneLineClass
|
20
|
+
@foo = 1
|
21
|
+
end
|
22
|
+
END
|
23
|
+
@roodi.check_content(content)
|
24
|
+
@roodi.errors.should be_empty
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should reject classes with more lines than the threshold" do
|
28
|
+
content = <<-END
|
29
|
+
class TwoLineClass
|
30
|
+
@foo = 1
|
31
|
+
@bar = 2
|
32
|
+
end
|
33
|
+
END
|
34
|
+
@roodi.check_content(content)
|
35
|
+
errors = @roodi.errors
|
36
|
+
errors.should_not be_empty
|
37
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Class \"TwoLineClass\" has 2 lines. It should have 1 or less.")
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ClassNameCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::ClassNameCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should accept camel case class names starting in capitals" do
|
9
|
+
content = <<-END
|
10
|
+
class GoodClassName
|
11
|
+
end
|
12
|
+
END
|
13
|
+
@roodi.check_content(content)
|
14
|
+
@roodi.errors.should be_empty
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be able to parse scoped class names" do
|
18
|
+
content = <<-END
|
19
|
+
class MyScope::GoodClassName
|
20
|
+
def method
|
21
|
+
end
|
22
|
+
end
|
23
|
+
END
|
24
|
+
# @roodi.print_content(content)
|
25
|
+
@roodi.check_content(content)
|
26
|
+
@roodi.errors.should be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should reject class names with underscores" do
|
30
|
+
content = <<-END
|
31
|
+
class Bad_ClassName
|
32
|
+
end
|
33
|
+
END
|
34
|
+
@roodi.check_content(content)
|
35
|
+
errors = @roodi.errors
|
36
|
+
errors.should_not be_empty
|
37
|
+
errors[0].to_s.should eql("dummy-file.rb:1 - Class name \"Bad_ClassName\" should match pattern /^[A-Z][a-zA-Z0-9]*$/")
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ClassVariableCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::ClassVariableCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should reject class variables" do
|
9
|
+
content = <<-END
|
10
|
+
@@foo
|
11
|
+
END
|
12
|
+
@roodi.check_content(content)
|
13
|
+
errors = @roodi.errors
|
14
|
+
errors.should_not be_empty
|
15
|
+
errors[0].to_s.should match(/dummy-file.rb:[1-2] - Don't use class variables. You might want to try a different design./)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::ControlCouplingCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::ControlCouplingCheck.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should reject methods with if checks using a parameter" do
|
9
|
+
content = <<-END
|
10
|
+
def write(quoted, foo)
|
11
|
+
if quoted
|
12
|
+
write_quoted(@value)
|
13
|
+
else
|
14
|
+
puts @value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
END
|
18
|
+
@roodi.check_content(content)
|
19
|
+
errors = @roodi.errors
|
20
|
+
errors.should_not be_empty
|
21
|
+
errors[0].to_s.should match(/dummy-file.rb:[2-3] - Method \"write\" uses the argument \"quoted\" for internal control./)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
describe Roodi::Checks::CyclomaticComplexityBlockCheck do
|
4
|
+
before(:each) do
|
5
|
+
@roodi = Roodi::Core::Runner.new(Roodi::Checks::CyclomaticComplexityBlockCheck.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 match(/dummy-file.rb:[2-4] - Block cyclomatic complexity is #{complexity}. It should be 0 or less./)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should find a simple block" do
|
16
|
+
content = <<-END
|
17
|
+
def method_name
|
18
|
+
it "should be a simple block" do
|
19
|
+
call_foo
|
20
|
+
end
|
21
|
+
end
|
22
|
+
END
|
23
|
+
verify_content_complexity(content, 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should find a block with multiple paths" do
|
27
|
+
content = <<-END
|
28
|
+
def method_name
|
29
|
+
it "should be a complex block" do
|
30
|
+
call_foo if some_condition
|
31
|
+
end
|
32
|
+
end
|
33
|
+
END
|
34
|
+
verify_content_complexity(content, 2)
|
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
|
67
|
+
end
|