simplabs-excellent 1.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.
Files changed (53) hide show
  1. data/History.txt +3 -0
  2. data/README.markdown +30 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/excellent +21 -0
  5. data/lib/simplabs/excellent.rb +12 -0
  6. data/lib/simplabs/excellent/checks.rb +16 -0
  7. data/lib/simplabs/excellent/checks/abc_metric_method_check.rb +80 -0
  8. data/lib/simplabs/excellent/checks/assignment_in_conditional_check.rb +38 -0
  9. data/lib/simplabs/excellent/checks/base.rb +42 -0
  10. data/lib/simplabs/excellent/checks/case_missing_else_check.rb +25 -0
  11. data/lib/simplabs/excellent/checks/class_line_count_check.rb +34 -0
  12. data/lib/simplabs/excellent/checks/class_name_check.rb +37 -0
  13. data/lib/simplabs/excellent/checks/class_variable_check.rb +25 -0
  14. data/lib/simplabs/excellent/checks/control_coupling_check.rb +32 -0
  15. data/lib/simplabs/excellent/checks/cyclomatic_complexity_block_check.rb +32 -0
  16. data/lib/simplabs/excellent/checks/cyclomatic_complexity_check.rb +39 -0
  17. data/lib/simplabs/excellent/checks/cyclomatic_complexity_method_check.rb +33 -0
  18. data/lib/simplabs/excellent/checks/empty_rescue_body_check.rb +39 -0
  19. data/lib/simplabs/excellent/checks/for_loop_check.rb +25 -0
  20. data/lib/simplabs/excellent/checks/line_count_check.rb +45 -0
  21. data/lib/simplabs/excellent/checks/method_line_count_check.rb +34 -0
  22. data/lib/simplabs/excellent/checks/method_name_check.rb +34 -0
  23. data/lib/simplabs/excellent/checks/module_line_count_check.rb +34 -0
  24. data/lib/simplabs/excellent/checks/module_name_check.rb +34 -0
  25. data/lib/simplabs/excellent/checks/name_check.rb +32 -0
  26. data/lib/simplabs/excellent/checks/parameter_number_check.rb +37 -0
  27. data/lib/simplabs/excellent/core.rb +2 -0
  28. data/lib/simplabs/excellent/core/checking_visitor.rb +34 -0
  29. data/lib/simplabs/excellent/core/error.rb +31 -0
  30. data/lib/simplabs/excellent/core/extensions/underscore.rb +27 -0
  31. data/lib/simplabs/excellent/core/iterator_visitor.rb +29 -0
  32. data/lib/simplabs/excellent/core/parse_tree_runner.rb +88 -0
  33. data/lib/simplabs/excellent/core/parser.rb +33 -0
  34. data/lib/simplabs/excellent/core/visitable_sexp.rb +31 -0
  35. data/spec/checks/abc_metric_method_check_spec.rb +94 -0
  36. data/spec/checks/assignment_in_conditional_check_spec.rb +73 -0
  37. data/spec/checks/case_missing_else_check_spec.rb +42 -0
  38. data/spec/checks/class_line_count_check_spec.rb +49 -0
  39. data/spec/checks/class_name_check_spec.rb +48 -0
  40. data/spec/checks/class_variable_check_spec.rb +26 -0
  41. data/spec/checks/control_coupling_check_spec.rb +32 -0
  42. data/spec/checks/cyclomatic_complexity_block_check_spec.rb +51 -0
  43. data/spec/checks/cyclomatic_complexity_method_check_spec.rb +184 -0
  44. data/spec/checks/empty_rescue_body_check_spec.rb +132 -0
  45. data/spec/checks/for_loop_check_spec.rb +52 -0
  46. data/spec/checks/method_line_count_check_spec.rb +50 -0
  47. data/spec/checks/method_name_check_spec.rb +91 -0
  48. data/spec/checks/module_line_count_check_spec.rb +49 -0
  49. data/spec/checks/module_name_check_spec.rb +37 -0
  50. data/spec/checks/parameter_number_check_spec.rb +61 -0
  51. data/spec/core/extensions/underscore_spec.rb +13 -0
  52. data/spec/spec_helper.rb +11 -0
  53. metadata +115 -0
@@ -0,0 +1,48 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::ClassNameCheck do
4
+
5
+ before(:each) do
6
+ @excellent = Simplabs::Excellent::Core::ParseTreeRunner.new(Simplabs::Excellent::Checks::ClassNameCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept camel case class names starting in capitals' do
12
+ content = <<-END
13
+ class GoodClassName; end
14
+ END
15
+ @excellent.check_content(content)
16
+
17
+ @excellent.errors.should be_empty
18
+ end
19
+
20
+ it 'should be able to parse scoped class names' do
21
+ content = <<-END
22
+ class MyScope::GoodClassName
23
+ def method
24
+ end
25
+ end
26
+ END
27
+ @excellent.check_content(content)
28
+
29
+ @excellent.errors.should be_empty
30
+ end
31
+
32
+ it 'should reject class names with underscores' do
33
+ content = <<-END
34
+ class Bad_ClassName
35
+ end
36
+ END
37
+ @excellent.check_content(content)
38
+ errors = @excellent.errors
39
+
40
+ errors.should_not be_empty
41
+ errors[0].info.should == { :class => :Bad_ClassName }
42
+ errors[0].line_number.should == 1
43
+ errors[0].message.should == 'Bad class name Bad_ClassName.'
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::ClassVariableCheck do
4
+
5
+ before(:each) do
6
+ @excellent = Simplabs::Excellent::Core::ParseTreeRunner.new(Simplabs::Excellent::Checks::ClassVariableCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should reject class variables' do
12
+ content = <<-END
13
+ @@foo
14
+ END
15
+ @excellent.check_content(content)
16
+ errors = @excellent.errors
17
+
18
+ errors.should_not be_empty
19
+ errors[0].info.should == { :variable => :@@foo }
20
+ errors[0].line_number.should == 1
21
+ errors[0].message.should == 'Class variable @@foo.'
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::ControlCouplingCheck do
4
+
5
+ before(:each) do
6
+ @excellent = Simplabs::Excellent::Core::ParseTreeRunner.new(Simplabs::Excellent::Checks::ControlCouplingCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should reject methods with if checks using a parameter' do
12
+ content = <<-END
13
+ def write(quoted, foo)
14
+ if quoted
15
+ write_quoted(@value)
16
+ else
17
+ puts @value
18
+ end
19
+ end
20
+ END
21
+ @excellent.check_content(content)
22
+ errors = @excellent.errors
23
+
24
+ errors.should_not be_empty
25
+ errors[0].info.should == { :method => :write, :argument => :quoted }
26
+ errors[0].line_number.should == 2
27
+ errors[0].message.should == 'Control of write is coupled to quoted.'
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::CyclomaticComplexityBlockCheck do
4
+
5
+ before(:each) do
6
+ @excellent = Simplabs::Excellent::Core::ParseTreeRunner.new(Simplabs::Excellent::Checks::CyclomaticComplexityBlockCheck.new({ :threshold => 0 }))
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should find a simple block' do
12
+ content = <<-END
13
+ def method_name
14
+ it 'should be a simple block' do
15
+ call_foo
16
+ end
17
+ end
18
+ END
19
+
20
+ verify_content_complexity(content, 1)
21
+ end
22
+
23
+ it 'should find a block with multiple paths' do
24
+ content = <<-END
25
+ def method_name
26
+ it 'should be a complex block' do
27
+ if some_condition
28
+ call_foo
29
+ else
30
+ call_bar
31
+ end
32
+ end
33
+ end
34
+ END
35
+
36
+ verify_content_complexity(content, 2)
37
+ end
38
+
39
+ end
40
+
41
+ def verify_content_complexity(content, score)
42
+ @excellent.check_content(content)
43
+ errors = @excellent.errors
44
+
45
+ errors.should_not be_empty
46
+ errors[0].info.should == { :score => score }
47
+ errors[0].line_number.should == 2
48
+ errors[0].message.should == "Block has cyclomatic complexity of #{score}."
49
+ end
50
+
51
+ end
@@ -0,0 +1,184 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::CyclomaticComplexityMethodCheck do
4
+
5
+ before do
6
+ @excellent = Simplabs::Excellent::Core::ParseTreeRunner.new(Simplabs::Excellent::Checks::CyclomaticComplexityMethodCheck.new({ :threshold => 0 }))
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should find an if block' do
12
+ content = <<-END
13
+ def method_name
14
+ call_foo if some_condition
15
+ end
16
+ END
17
+
18
+ verify_content_complexity(content, 2)
19
+ end
20
+
21
+ it 'should find an unless block' do
22
+ content = <<-END
23
+ def method_name
24
+ call_foo unless some_condition
25
+ end
26
+ END
27
+
28
+ verify_content_complexity(content, 2)
29
+ end
30
+
31
+ it 'should find an elsif block' do
32
+ content = <<-END
33
+ def method_name
34
+ if first_condition then
35
+ call_foo
36
+ elsif second_condition then
37
+ call_bar
38
+ else
39
+ call_bam
40
+ end
41
+ end
42
+ END
43
+
44
+ verify_content_complexity(content, 3)
45
+ end
46
+
47
+ it 'should find a ternary operator' do
48
+ content = <<-END
49
+ def method_name
50
+ value = some_condition ? 1 : 2
51
+ end
52
+ END
53
+
54
+ verify_content_complexity(content, 2)
55
+ end
56
+
57
+ it 'should find a while loop' do
58
+ content = <<-END
59
+ def method_name
60
+ while some_condition do
61
+ call_foo
62
+ end
63
+ end
64
+ END
65
+
66
+ verify_content_complexity(content, 2)
67
+ end
68
+
69
+ it 'should find an until loop' do
70
+ content = <<-END
71
+ def method_name
72
+ until some_condition do
73
+ call_foo
74
+ end
75
+ end
76
+ END
77
+
78
+ verify_content_complexity(content, 2)
79
+ end
80
+
81
+ it 'should find a for loop' do
82
+ content = <<-END
83
+ def method_name
84
+ for i in 1..2 do
85
+ call_method
86
+ end
87
+ end
88
+ END
89
+
90
+ verify_content_complexity(content, 2)
91
+ end
92
+
93
+ it 'should find a rescue block' do
94
+ content = <<-END
95
+ def method_name
96
+ begin
97
+ call_foo
98
+ rescue Exception
99
+ call_bar
100
+ end
101
+ end
102
+ END
103
+
104
+ verify_content_complexity(content, 2)
105
+ end
106
+
107
+ it 'should find a case and when block' do
108
+ content = <<-END
109
+ def method_name
110
+ case value
111
+ when 1
112
+ call_foo
113
+ when 2
114
+ call_bar
115
+ end
116
+ end
117
+ END
118
+
119
+ verify_content_complexity(content, 4)
120
+ end
121
+
122
+ describe 'when processing operators' do
123
+
124
+ ['&&', 'and', '||', 'or'].each do |operator|
125
+
126
+ it "should find #{operator}" do
127
+ content = <<-END
128
+ def method_name
129
+ call_foo #{operator} call_bar
130
+ end
131
+ END
132
+
133
+ verify_content_complexity(content, 2)
134
+ end
135
+
136
+ end
137
+
138
+ end
139
+
140
+ it 'should deal with nested if blocks containing && and ||' do
141
+ content = <<-END
142
+ def method_name
143
+ if first_condition then
144
+ call_foo if second_condition && third_condition
145
+ call_bar if fourth_condition || fifth_condition
146
+ end
147
+ end
148
+ END
149
+
150
+ verify_content_complexity(content, 6)
151
+ end
152
+
153
+ it 'should count stupid nested if and else blocks' do
154
+ content = <<-END
155
+ def method_name
156
+ if first_condition then
157
+ call_foo
158
+ else
159
+ if second_condition then
160
+ call_bar
161
+ else
162
+ call_bam if third_condition
163
+ end
164
+ call_baz if fourth_condition
165
+ end
166
+ end
167
+ END
168
+
169
+ verify_content_complexity(content, 5)
170
+ end
171
+
172
+ end
173
+
174
+ def verify_content_complexity(content, score)
175
+ @excellent.check_content(content)
176
+ errors = @excellent.errors
177
+
178
+ errors.should_not be_empty
179
+ errors[0].info.should == { :method => :method_name, :score => score }
180
+ errors[0].line_number.should == 1
181
+ errors[0].message.should == "Method method_name has cyclomatic complexity of #{score}."
182
+ end
183
+
184
+ end
@@ -0,0 +1,132 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Simplabs::Excellent::Checks::EmptyRescueBodyCheck do
4
+
5
+ before do
6
+ @excellent = Simplabs::Excellent::Core::ParseTreeRunner.new(Simplabs::Excellent::Checks::EmptyRescueBodyCheck.new)
7
+ end
8
+
9
+ describe '#evaluate' do
10
+
11
+ it 'should accept a rescue body with content and no parameter' do
12
+ content = <<-END
13
+ begin
14
+ call_method
15
+ rescue
16
+ puts "Recover from the call"
17
+ end
18
+ END
19
+ @excellent.check_content(content)
20
+
21
+ @excellent.errors.should be_empty
22
+ end
23
+
24
+ it 'should accept a rescue body with a return' do
25
+ content = <<-END
26
+ begin
27
+ call_method
28
+ rescue
29
+ return true
30
+ end
31
+ END
32
+ @excellent.check_content(content)
33
+
34
+ @excellent.errors.should be_empty
35
+ end
36
+
37
+ it "should accept a virtual method call" do
38
+ content = <<-END
39
+ begin
40
+ call_method
41
+ rescue
42
+ show_error
43
+ end
44
+ END
45
+ @excellent.check_content(content)
46
+
47
+ @excellent.errors.should be_empty
48
+ end
49
+
50
+ it 'should accept a rescue body with content and a parameter' do
51
+ content = <<-END
52
+ begin
53
+ call_method
54
+ rescue Exception => e
55
+ puts "Recover from the call"
56
+ end
57
+ END
58
+ @excellent.check_content(content)
59
+
60
+ @excellent.errors.should be_empty
61
+ end
62
+
63
+ it 'should accept a rescue body with an assignment' do
64
+ content = <<-END
65
+ begin
66
+ call_method
67
+ rescue Exception => e
68
+ my_var = 1
69
+ end
70
+ END
71
+ @excellent.check_content(content)
72
+
73
+ @excellent.errors.should be_empty
74
+ end
75
+
76
+ it 'should accept a rescue body with an attribute assignment' do
77
+ content = <<-END
78
+ begin
79
+ call_method
80
+ rescue Exception => e
81
+ self.var = 1
82
+ end
83
+ END
84
+ @excellent.check_content(content)
85
+
86
+ @excellent.errors.should be_empty
87
+ end
88
+
89
+ it 'should accept an inline rescue statement' do
90
+ content = <<-END
91
+ value = call_method rescue 1
92
+ END
93
+ @excellent.check_content(content)
94
+
95
+ @excellent.errors.should be_empty
96
+ end
97
+
98
+ it 'should reject an empty rescue block with no parameter' do
99
+ content = <<-END
100
+ begin
101
+ call_method
102
+ rescue
103
+ end
104
+ END
105
+
106
+ verify_error_found(content)
107
+ end
108
+
109
+ it 'should reject an empty rescue block with a parameter' do
110
+ content = <<-END
111
+ begin
112
+ call_method
113
+ rescue Exception => e
114
+ end
115
+ END
116
+
117
+ verify_error_found(content)
118
+ end
119
+
120
+ end
121
+
122
+ def verify_error_found(content)
123
+ @excellent.check_content(content)
124
+ errors = @excellent.errors
125
+
126
+ errors.should_not be_empty
127
+ errors[0].info.should == {}
128
+ errors[0].line_number.should == 3
129
+ errors[0].message.should == 'Rescue block is empty.'
130
+ end
131
+
132
+ end