reek 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/History.txt +44 -4
  2. data/License.txt +20 -0
  3. data/README.rdoc +83 -0
  4. data/Rakefile +0 -1
  5. data/bin/reek +3 -11
  6. data/config/defaults.reek +20 -1
  7. data/features/masking_smells.feature +111 -0
  8. data/features/options.feature +49 -0
  9. data/features/reports.feature +90 -0
  10. data/features/samples.feature +284 -0
  11. data/features/stdin.feature +43 -0
  12. data/features/step_definitions/reek_steps.rb +35 -0
  13. data/features/support/env.rb +38 -0
  14. data/lib/reek.rb +1 -1
  15. data/lib/reek/adapters/application.rb +47 -0
  16. data/lib/reek/adapters/config_file.rb +31 -0
  17. data/lib/reek/adapters/core_extras.rb +72 -0
  18. data/lib/reek/{object_source.rb → adapters/object_source.rb} +15 -19
  19. data/lib/reek/{rake_task.rb → adapters/rake_task.rb} +2 -2
  20. data/lib/reek/adapters/report.rb +91 -0
  21. data/lib/reek/adapters/source.rb +53 -0
  22. data/lib/reek/{spec.rb → adapters/spec.rb} +45 -60
  23. data/lib/reek/block_context.rb +1 -1
  24. data/lib/reek/class_context.rb +26 -6
  25. data/lib/reek/code_context.rb +8 -0
  26. data/lib/reek/code_parser.rb +82 -39
  27. data/lib/reek/command_line.rb +85 -0
  28. data/lib/reek/configuration.rb +51 -0
  29. data/lib/reek/detector_stack.rb +39 -0
  30. data/lib/reek/exceptions.reek +8 -1
  31. data/lib/reek/method_context.rb +53 -11
  32. data/lib/reek/module_context.rb +1 -2
  33. data/lib/reek/name.rb +8 -2
  34. data/lib/reek/sexp_formatter.rb +2 -0
  35. data/lib/reek/smell_warning.rb +26 -8
  36. data/lib/reek/smells/control_couple.rb +8 -4
  37. data/lib/reek/smells/data_clump.rb +88 -0
  38. data/lib/reek/smells/duplication.rb +11 -9
  39. data/lib/reek/smells/feature_envy.rb +3 -4
  40. data/lib/reek/smells/large_class.rb +17 -17
  41. data/lib/reek/smells/long_method.rb +10 -8
  42. data/lib/reek/smells/long_parameter_list.rb +16 -10
  43. data/lib/reek/smells/long_yield_list.rb +1 -1
  44. data/lib/reek/smells/nested_iterators.rb +3 -3
  45. data/lib/reek/smells/simulated_polymorphism.rb +58 -0
  46. data/lib/reek/smells/smell_detector.rb +94 -27
  47. data/lib/reek/smells/uncommunicative_name.rb +23 -23
  48. data/lib/reek/smells/utility_function.rb +27 -11
  49. data/lib/reek/sniffer.rb +183 -0
  50. data/reek.gemspec +5 -5
  51. data/spec/quality/reek_source_spec.rb +15 -0
  52. data/spec/reek/adapters/report_spec.rb +49 -0
  53. data/spec/reek/adapters/should_reek_of_spec.rb +108 -0
  54. data/spec/reek/adapters/should_reek_only_of_spec.rb +87 -0
  55. data/spec/reek/adapters/should_reek_spec.rb +92 -0
  56. data/spec/reek/block_context_spec.rb +7 -1
  57. data/spec/reek/class_context_spec.rb +39 -16
  58. data/spec/reek/code_context_spec.rb +7 -7
  59. data/spec/reek/code_parser_spec.rb +6 -1
  60. data/spec/reek/config_spec.rb +3 -3
  61. data/spec/reek/configuration_spec.rb +12 -0
  62. data/spec/reek/method_context_spec.rb +2 -2
  63. data/spec/reek/name_spec.rb +24 -0
  64. data/spec/reek/object_source_spec.rb +23 -0
  65. data/spec/reek/singleton_method_context_spec.rb +2 -2
  66. data/spec/reek/smell_warning_spec.rb +53 -0
  67. data/spec/reek/smells/data_clump_spec.rb +87 -0
  68. data/spec/reek/smells/duplication_spec.rb +13 -17
  69. data/spec/reek/smells/feature_envy_spec.rb +23 -28
  70. data/spec/reek/smells/large_class_spec.rb +109 -34
  71. data/spec/reek/smells/long_method_spec.rb +140 -3
  72. data/spec/reek/smells/long_parameter_list_spec.rb +1 -2
  73. data/spec/reek/smells/simulated_polymorphism_spec.rb +50 -0
  74. data/spec/reek/smells/smell_detector_spec.rb +53 -0
  75. data/spec/reek/smells/uncommunicative_name_spec.rb +20 -7
  76. data/spec/reek/smells/utility_function_spec.rb +76 -67
  77. data/spec/reek/sniffer_spec.rb +10 -0
  78. data/spec/samples/all_but_one_masked/clean_one.rb +6 -0
  79. data/spec/samples/all_but_one_masked/dirty.rb +7 -0
  80. data/spec/samples/all_but_one_masked/masked.reek +5 -0
  81. data/spec/samples/clean_due_to_masking/clean_one.rb +6 -0
  82. data/spec/samples/clean_due_to_masking/clean_three.rb +6 -0
  83. data/spec/samples/clean_due_to_masking/clean_two.rb +6 -0
  84. data/spec/samples/clean_due_to_masking/dirty_one.rb +7 -0
  85. data/spec/samples/clean_due_to_masking/dirty_two.rb +7 -0
  86. data/spec/samples/clean_due_to_masking/masked.reek +7 -0
  87. data/spec/samples/corrupt_config_file/corrupt.reek +1 -0
  88. data/spec/samples/corrupt_config_file/dirty.rb +7 -0
  89. data/spec/samples/empty_config_file/dirty.rb +7 -0
  90. data/spec/samples/empty_config_file/empty.reek +0 -0
  91. data/spec/samples/exceptions.reek +4 -0
  92. data/spec/{slow/samples → samples}/inline.rb +0 -0
  93. data/spec/samples/masked/dirty.rb +7 -0
  94. data/spec/samples/masked/masked.reek +3 -0
  95. data/spec/samples/mixed_results/clean_one.rb +6 -0
  96. data/spec/samples/mixed_results/clean_three.rb +6 -0
  97. data/spec/samples/mixed_results/clean_two.rb +6 -0
  98. data/spec/samples/mixed_results/dirty_one.rb +7 -0
  99. data/spec/samples/mixed_results/dirty_two.rb +7 -0
  100. data/spec/samples/not_quite_masked/dirty.rb +8 -0
  101. data/spec/samples/not_quite_masked/masked.reek +5 -0
  102. data/spec/{slow/samples → samples}/optparse.rb +0 -0
  103. data/spec/samples/overrides/masked/dirty.rb +7 -0
  104. data/spec/samples/overrides/masked/lower.reek +5 -0
  105. data/spec/samples/overrides/upper.reek +5 -0
  106. data/spec/{slow/samples → samples}/redcloth.rb +0 -0
  107. data/spec/samples/three_clean_files/clean_one.rb +6 -0
  108. data/spec/samples/three_clean_files/clean_three.rb +6 -0
  109. data/spec/samples/three_clean_files/clean_two.rb +6 -0
  110. data/spec/samples/two_smelly_files/dirty_one.rb +7 -0
  111. data/spec/samples/two_smelly_files/dirty_two.rb +7 -0
  112. data/spec/spec.opts +1 -1
  113. data/spec/spec_helper.rb +4 -4
  114. data/tasks/reek.rake +8 -5
  115. data/tasks/test.rake +51 -0
  116. metadata +75 -25
  117. data/README.txt +0 -6
  118. data/lib/reek/options.rb +0 -92
  119. data/lib/reek/report.rb +0 -81
  120. data/lib/reek/smells/smells.rb +0 -81
  121. data/lib/reek/source.rb +0 -127
  122. data/spec/reek/options_spec.rb +0 -13
  123. data/spec/reek/report_spec.rb +0 -48
  124. data/spec/reek/smells/smell_spec.rb +0 -24
  125. data/spec/slow/inline_spec.rb +0 -43
  126. data/spec/slow/optparse_spec.rb +0 -108
  127. data/spec/slow/redcloth_spec.rb +0 -101
  128. data/spec/slow/reek_source_spec.rb +0 -20
  129. data/spec/slow/script_spec.rb +0 -55
  130. data/spec/slow/source_list_spec.rb +0 -40
  131. data/tasks/rspec.rake +0 -21
@@ -0,0 +1,87 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ require 'reek/smells/data_clump'
4
+
5
+ include Reek::Smells
6
+
7
+ describe DataClump do
8
+ it 'should not report small parameter sets' do
9
+ src = <<EOS
10
+ class Scrunch
11
+ def first(pa) @field == :sym ? 0 : 3; end
12
+ def second(pa) @field == :sym; end
13
+ def third(pa) pa - pb + @fred; end
14
+ end
15
+ EOS
16
+
17
+ src.should_not reek
18
+ end
19
+
20
+ it 'should report 3 identical pairs in a class' do
21
+ src = <<EOS
22
+ class Scrunch
23
+ def first(pa, pb) @field == :sym ? 0 : 3; end
24
+ def second(pa, pb) @field == :sym; end
25
+ def third(pa, pb) pa - pb + @fred; end
26
+ end
27
+ EOS
28
+
29
+ src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
30
+ end
31
+
32
+ it 'should report 3 swapped pairs in a class' do
33
+ src = <<EOS
34
+ class Scrunch
35
+ def one(pa, pb) @field == :sym ? 0 : 3; end
36
+ def two(pb, pa) @field == :sym; end
37
+ def tri(pa, pb) pa - pb + @fred; end
38
+ end
39
+ EOS
40
+
41
+ src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
42
+ end
43
+
44
+ it 'should report 3 identical parameter sets in a class' do
45
+ src = <<EOS
46
+ class Scrunch
47
+ def first(pa, pb, pc) @field == :sym ? 0 : 3; end
48
+ def second(pa, pb, pc) @field == :sym; end
49
+ def third(pa, pb, pc) pa - pb + @fred; end
50
+ end
51
+ EOS
52
+
53
+ src.should reek_of(:DataClump, /\[pa, pb, pc\]/, /3 methods/)
54
+ src.should_not reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
55
+ src.should_not reek_of(:DataClump, /\[pa, pc\]/, /3 methods/)
56
+ src.should_not reek_of(:DataClump, /\[pb, pc\]/, /3 methods/)
57
+ end
58
+
59
+ it 'should recognise re-ordered identical parameter sets' do
60
+ src = <<EOS
61
+ class Scrunch
62
+ def first(pb, pa, pc) @field == :sym ? 0 : 3; end
63
+ def second(pc, pb, pa) @field == :sym; end
64
+ def third(pa, pb, pc) pa - pb + @fred; end
65
+ end
66
+ EOS
67
+
68
+ src.should reek_of(:DataClump, /\[pa, pb, pc\]/, /3 methods/)
69
+ src.should_not reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
70
+ src.should_not reek_of(:DataClump, /\[pa, pc\]/, /3 methods/)
71
+ src.should_not reek_of(:DataClump, /\[pb, pc\]/, /3 methods/)
72
+ end
73
+
74
+ it 'should count only identical parameter sets' do
75
+ src = <<EOS
76
+ class RedCloth
77
+ def fa(p1, p2, p3, conten) end
78
+ def fb(p1, p2, p3, conten) end
79
+ def fc(name, windowW, windowH) end
80
+ end
81
+ EOS
82
+
83
+ src.should_not reek_of(:DataClump)
84
+ end
85
+
86
+ # TODO: include singleton methods in the calcs
87
+ end
@@ -1,5 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
- require 'reek/report'
2
+
3
+ require 'reek/configuration'
3
4
  require 'reek/method_context'
4
5
  require 'reek/stop_context'
5
6
  require 'reek/smells/duplication'
@@ -21,7 +22,7 @@ describe Duplication, "repeated method calls" do
21
22
  end
22
23
 
23
24
  it 'should report nested calls' do
24
- ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.to_source
25
+ ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.sniff
25
26
  ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
26
27
  ruby.should reek_of(:Duplication, /@other.thing.foo/)
27
28
  end
@@ -41,41 +42,36 @@ describe Duplication, "non-repeated method calls" do
41
42
  end
42
43
  end
43
44
 
44
- require 'ostruct'
45
-
46
45
  describe Duplication, '#examine' do
47
46
  before :each do
48
- @mc = OpenStruct.new
47
+ @mc = MethodContext.new(StopContext.new, s(:defn, :fred))
49
48
  @dup = Duplication.new
50
49
  end
51
50
 
52
51
  it 'should return true when reporting a smell' do
53
- @mc.calls = {s(:call, nil, :other, s(:arglist)) => 47}
54
- @dup.examine(@mc, []).should == true
52
+ 3.times { @mc.record_call_to(s(:call, nil, :other, s(:arglist)))}
53
+ @dup.examine(@mc).should == true
55
54
  end
56
-
55
+
57
56
  it 'should return false when not reporting a smell' do
58
- @mc.calls = []
59
- @dup.examine(@mc, []).should == false
57
+ @dup.examine(@mc).should == false
60
58
  end
61
-
59
+
62
60
  it 'should return false when not reporting calls to new' do
63
- @mc.calls = {[:call, :Set, :new] => 4}
64
- @dup.examine(@mc, []).should == false
61
+ 4.times { @mc.record_call_to(s(:call, s(:Set), :new, s(:arglist)))}
62
+ @dup.examine(@mc).should == false
65
63
  end
66
64
  end
67
65
 
68
66
  describe Duplication, 'when disabled' do
69
67
  before :each do
70
68
  @ctx = MethodContext.new(StopContext.new, [0, :double_thing])
71
- @dup = Duplication.new({SmellDetector::ENABLED_KEY => false})
72
- @rpt = Report.new
69
+ @dup = Duplication.new({SmellConfiguration::ENABLED_KEY => false})
73
70
  end
74
71
 
75
72
  it 'should not report repeated call' do
76
73
  @ctx.record_call_to([:fred])
77
74
  @ctx.record_call_to([:fred])
78
- @dup.examine(@ctx, @rpt).should == false
79
- @rpt.length.should == 0
75
+ @dup.examine(@ctx).should == false
80
76
  end
81
77
  end
@@ -27,9 +27,9 @@ describe FeatureEnvy do
27
27
 
28
28
  it 'should not report return value' do
29
29
  'def no_envy(arga)
30
- arga.barg(@item)
31
- arga
32
- end'.should_not reek
30
+ arga.barg(@item)
31
+ arga
32
+ end'.should_not reek
33
33
  end
34
34
 
35
35
  it 'should report many calls to parameter' do
@@ -133,22 +133,6 @@ describe FeatureEnvy do
133
133
  end'.should_not reek
134
134
  end
135
135
 
136
- it 'reports the most-used ivar' do
137
- pending('bug')
138
- 'def func
139
- @other.a
140
- @other.b
141
- @nother.c
142
- end'.should reek_of(:FeatureEnvy, /@other/)
143
- #
144
- # def other.func(me)
145
- # a
146
- # b
147
- # me.nother_c
148
- # end
149
- #
150
- end
151
-
152
136
  it 'ignores multiple ivars' do
153
137
  'def func
154
138
  @other.a
@@ -177,12 +161,8 @@ describe FeatureEnvy do
177
161
  it 'counts self references correctly' do
178
162
  'def adopt!(other)
179
163
  other.keys.each do |key|
180
- ov = other[key]
181
- if Array === ov and has_key?(key)
182
- self[key] += ov
183
- else
184
- self[key] = ov
185
- end
164
+ self[key] += 3
165
+ self[key] = o4
186
166
  end
187
167
  self
188
168
  end'.should_not reek
@@ -202,7 +182,22 @@ def report
202
182
  @report
203
183
  end
204
184
  EOS
205
- ruby.should reek_only_of(:LongMethod)
185
+
186
+ ruby.should_not reek
187
+ end
188
+
189
+ it 'interprets << correctly' do
190
+ ruby = <<EOS
191
+ def report_on(report)
192
+ if @is_masked
193
+ report.record_masked_smell(self)
194
+ else
195
+ report << self
196
+ end
197
+ end
198
+ EOS
199
+
200
+ ruby.should_not reek
206
201
  end
207
202
  end
208
203
 
@@ -216,10 +211,10 @@ describe FeatureEnvy, '#examine' do
216
211
  it 'should return true when reporting a smell' do
217
212
  @context.refs.record_ref([:lvar, :thing])
218
213
  @context.refs.record_ref([:lvar, :thing])
219
- @fe.examine(@context, []).should == true
214
+ @fe.examine(@context).should == true
220
215
  end
221
216
 
222
217
  it 'should return false when not reporting a smell' do
223
- @fe.examine(@context, []).should == false
218
+ @fe.examine(@context).should == false
224
219
  end
225
220
  end
@@ -1,44 +1,67 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ require 'reek/class_context'
4
+ require 'reek/stop_context'
2
5
  require 'reek/code_parser'
3
- require 'reek/report'
4
6
  require 'reek/smells/large_class'
5
7
 
6
8
  include Reek
7
9
  include Reek::Smells
8
10
 
9
- describe LargeClass do
11
+ describe LargeClass, 'checking Class objects' do
10
12
 
11
- it 'should report large class' do
13
+ it 'should not report class with 26 methods' do
14
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
12
15
  class BigOne
13
16
  26.times do |i|
14
- define_method "method#{i}".to_sym do
17
+ define_method "method#{i}x".to_sym do
15
18
  @melting
16
19
  end
17
20
  end
18
21
  end
19
- BigOne.should reek_only_of(:LargeClass, /BigOne/)
22
+ BigOne.should_not reek
20
23
  end
21
- end
22
-
23
- describe LargeClass do
24
24
 
25
25
  it 'should not report short class' do
26
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
26
27
  class ShortClass
27
- def method1() @var1; end
28
- def method2() @var2; end
29
- def method3() @var3; end
30
- def method4() @var4; end
31
- def method5() @var5; end
32
- def method6() @var6; end
28
+ def method_a() @var_a; end
29
+ def method_b() @var_b; end
30
+ def method_c() @var_c; end
31
+ def method_d() @var_d; end
32
+ def method_e() @var_e; end
33
+ def method_f() @var_f; end
33
34
  end
34
35
  ShortClass.should_not reek
35
36
  end
37
+
38
+ describe LargeClass, 'counting instance variables' do
39
+ it 'should not report class with 10 ivars' do
40
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
41
+ class ManyIvars
42
+ def meth
43
+ @vara = @varb = @varc = @vard = @vare
44
+ @varf = @varg = @varh = @vari = @varj
45
+ end
46
+ end
47
+ ManyIvars.should_not reek
48
+ end
49
+
50
+ it 'ignores class with only a couple of ivars' do
51
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
52
+ LargeClass.should_not reek_of(:LargeClass)
53
+ end
54
+
55
+ it 'ignores fq class with only a couple of ivars' do
56
+ pending('test requires ParseTree') unless ObjectSource.can_parse_objects?
57
+ Reek::Smells::LargeClass.should_not reek_of(:LargeClass)
58
+ end
59
+ end
36
60
  end
37
61
 
38
62
  describe LargeClass, 'when exceptions are listed' do
39
63
 
40
64
  before(:each) do
41
- @rpt = Report.new
42
65
  @ctx = ClassContext.create(StopContext.new, [0, :Humungous])
43
66
  30.times { |num| @ctx.record_method("method#{num}") }
44
67
  @config = LargeClass.default_config
@@ -47,41 +70,93 @@ describe LargeClass, 'when exceptions are listed' do
47
70
  it 'should ignore first excepted name' do
48
71
  @config[LargeClass::EXCLUDE_KEY] = ['Humungous']
49
72
  lc = LargeClass.new(@config)
50
- lc.examine(@ctx, @rpt).should == false
51
- @rpt.length.should == 0
73
+ lc.examine(@ctx).should == false
52
74
  end
53
75
 
54
76
  it 'should ignore second excepted name' do
55
77
  @config[LargeClass::EXCLUDE_KEY] = ['Oversized', 'Humungous']
56
78
  lc = LargeClass.new(@config)
57
- lc.examine(@ctx, @rpt).should == false
58
- @rpt.length.should == 0
79
+ lc.examine(@ctx).should == false
59
80
  end
60
81
 
61
82
  it 'should report non-excepted name' do
62
83
  @config[LargeClass::EXCLUDE_KEY] = ['SmellMe']
63
84
  lc = LargeClass.new(@config)
64
- lc.examine(@ctx, @rpt).should == true
65
- @rpt.length.should == 1
85
+ lc.examine(@ctx).should == true
66
86
  end
67
87
  end
68
88
 
69
- describe LargeClass, 'counting instance variables' do
70
- it 'warns about class with 10 ivars' do
71
- class ManyIvars
72
- def method
73
- @vara = @varb = @varc = @vard = @vare
74
- @varf = @varg = @varh = @vari = @varj
75
- end
89
+ describe LargeClass, 'checking source code' do
90
+
91
+ describe 'counting instance variables' do
92
+ it 'should not report empty class' do
93
+ ClassContext.from_s('class Empty;end').should have(0).variable_names
94
+ end
95
+
96
+ it 'should count ivars in one method' do
97
+ ClassContext.from_s('class Empty;def ivars() @aa=@ab=@ac=@ad; end;end').should have(4).variable_names
98
+ end
99
+
100
+ it 'should count ivars in 2 methods' do
101
+ ClassContext.from_s('class Empty;def iv1() @aa=@ab; end;def iv2() @aa=@ac; end;end').should have(3).variable_names
102
+ end
103
+
104
+ it 'should not report 9 ivars' do
105
+ 'class Empty;def ivars() @aa=@ab=@ac=@ad=@ae=@af=@ag=@ah=@ai; end;end'.should_not reek
106
+ end
107
+
108
+ it 'should report 10 ivars' do
109
+ 'class Empty;def ivars() @aa=@ab=@ac=@ad=@ae=@af=@ag=@ah=@ai=@aj; end;end'.should reek_of(:LargeClass)
76
110
  end
77
- ManyIvars.should reek_of(:LargeClass, /10/)
78
- end
79
111
 
80
- it 'ignores class with only a couple of ivars' do
81
- LargeClass.should_not reek_of(:LargeClass)
112
+ it 'should not report 10 ivars in 2 extensions' do
113
+ src = <<EOS
114
+ class Full;def ivars_a() @aa=@ab=@ac=@ad=@ae; end;end
115
+ class Full;def ivars_b() @af=@ag=@ah=@ai=@aj; end;end
116
+ EOS
117
+
118
+ src.should_not reek
119
+ end
82
120
  end
83
121
 
84
- it 'ignores fq class with only a couple of ivars' do
85
- Reek::Smells::LargeClass.should_not reek_of(:LargeClass)
122
+ describe 'counting methods' do
123
+ it 'should not report empty class' do
124
+ ClassContext.from_s('class Empty;end').num_methods.should == 0
125
+ end
126
+
127
+ it 'should count 1 method' do
128
+ ClassContext.from_s('class Empty;def ivars() @aa=@ab; end;end').num_methods.should == 1
129
+ end
130
+
131
+ it 'should count 2 methods' do
132
+ ClassContext.from_s('class Empty;def meth1() @aa=@ab;end;def meth2() @aa=@ab;end;end').num_methods.should == 2
133
+ end
134
+
135
+ it 'should not report 25 methods' do
136
+ src = <<EOS
137
+ class Full
138
+ def me01x()3 end;def me02x()3 end;def me03x()3 end;def me04x()3 end;def me05x()3 end
139
+ def me11x()3 end;def me12x()3 end;def me13x()3 end;def me14x()3 end;def me15x()3 end
140
+ def me21x()3 end;def me22x()3 end;def me23x()3 end;def me24x()3 end;def me25x()3 end
141
+ def me31x()3 end;def me32x()3 end;def me33x()3 end;def me34x()3 end;def me35x()3 end
142
+ def me41x()3 end;def me42x()3 end;def me43x()3 end;def me44x()3 end;def me45x()3 end
143
+ end
144
+ EOS
145
+ src.should_not reek
146
+ end
147
+
148
+ it 'should report 26 methods' do
149
+ src = <<EOS
150
+ class Full
151
+ def me01x()3 end;def me02x()3 end;def me03x()3 end;def me04x()3 end;def me05x()3 end
152
+ def me11x()3 end;def me12x()3 end;def me13x()3 end;def me14x()3 end;def me15x()3 end
153
+ def me21x()3 end;def me22x()3 end;def me23x()3 end;def me24x()3 end;def me25x()3 end
154
+ def me31x()3 end;def me32x()3 end;def me33x()3 end;def me34x()3 end;def me35x()3 end
155
+ def me41x()3 end;def me42x()3 end;def me43x()3 end;def me44x()3 end;def me45x()3 end
156
+ def me51x()3 end
157
+ end
158
+ EOS
159
+ src.should reek_of(:LargeClass)
160
+ end
86
161
  end
87
162
  end
@@ -1,9 +1,17 @@
1
1
  require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
2
 
3
3
  require 'reek/code_parser'
4
- require 'reek/report'
4
+ require 'reek/sniffer'
5
+ require 'reek/smells/long_method'
5
6
 
6
7
  include Reek
8
+ include Reek::Smells
9
+
10
+ def process_method(src)
11
+ source = src.to_reek_source
12
+ sniffer = Sniffer.new(source)
13
+ CodeParser.new(sniffer).process_defn(source.syntax_tree)
14
+ end
7
15
 
8
16
  describe LongMethod do
9
17
  it 'should not report short methods' do
@@ -19,7 +27,7 @@ describe LongMethod do
19
27
  end
20
28
 
21
29
  it 'should only report a long method once' do
22
- source =<<EOS
30
+ src = <<EOS
23
31
  def standard_entries(rbconfig)
24
32
  @abc = rbconfig
25
33
  rubypath = File.join(@abc['bindir'], @abcf['ruby_install_name'] + cff['EXEEXT'])
@@ -37,7 +45,7 @@ def standard_entries(rbconfig)
37
45
  end
38
46
  end
39
47
  EOS
40
- source.should reek_only_of(:LongMethod)
48
+ src.should reek_only_of(:LongMethod)
41
49
  end
42
50
 
43
51
  it 'should report long inner block' do
@@ -57,3 +65,132 @@ EOS
57
65
  src.should reek_only_of(:LongMethod)
58
66
  end
59
67
  end
68
+
69
+ describe LongMethod do
70
+ it 'counts 1 assignment' do
71
+ method = process_method('def one() val = 4; end')
72
+ method.num_statements.should == 1
73
+ end
74
+
75
+ it 'counts 3 assignments' do
76
+ method = process_method('def one() val = 4; val = 4; val = 4; end')
77
+ method.num_statements.should == 3
78
+ end
79
+
80
+ it 'counts 1 attr assignment' do
81
+ method = process_method('def one() val[0] = 4; end')
82
+ method.num_statements.should == 1
83
+ end
84
+
85
+ it 'counts 1 increment assignment' do
86
+ method = process_method('def one() val += 4; end')
87
+ method.num_statements.should == 1
88
+ end
89
+
90
+ it 'counts 1 increment attr assignment' do
91
+ method = process_method('def one() val[0] += 4; end')
92
+ method.num_statements.should == 1
93
+ end
94
+
95
+ it 'counts 1 nested assignment' do
96
+ method = process_method('def one() val = fred = 4; end')
97
+ method.num_statements.should == 1
98
+ end
99
+
100
+ it 'counts returns' do
101
+ method = process_method('def one() val = 4; true; end')
102
+ method.num_statements.should == 2
103
+ end
104
+ end
105
+
106
+ describe LongMethod, 'does not count control statements' do
107
+ it 'counts 1 statement in a conditional expression' do
108
+ method = process_method('def one() if val == 4; callee(); end; end')
109
+ method.num_statements.should == 1
110
+ end
111
+
112
+ it 'counts 3 statements in a conditional expression' do
113
+ method = process_method('def one() if val == 4; callee(); callee(); callee(); end; end')
114
+ method.num_statements.should == 3
115
+ end
116
+
117
+ it 'does not count empty conditional expression' do
118
+ method = process_method('def one() if val == 4; ; end; end')
119
+ method.num_statements.should == 0
120
+ end
121
+
122
+ it 'counts 1 statement in a while loop' do
123
+ method = process_method('def one() while val < 4; callee(); end; end')
124
+ method.num_statements.should == 1
125
+ end
126
+
127
+ it 'counts 3 statements in a while loop' do
128
+ method = process_method('def one() while val < 4; callee(); callee(); callee(); end; end')
129
+ method.num_statements.should == 3
130
+ end
131
+
132
+ it 'counts 1 statement in a until loop' do
133
+ method = process_method('def one() until val < 4; callee(); end; end')
134
+ method.num_statements.should == 1
135
+ end
136
+
137
+ it 'counts 3 statements in a until loop' do
138
+ method = process_method('def one() until val < 4; callee(); callee(); callee(); end; end')
139
+ method.num_statements.should == 3
140
+ end
141
+
142
+ it 'counts 1 statement in a for loop' do
143
+ method = process_method('def one() for i in 0..4; callee(); end; end')
144
+ method.num_statements.should == 1
145
+ end
146
+
147
+ it 'counts 3 statements in a for loop' do
148
+ method = process_method('def one() for i in 0..4; callee(); callee(); callee(); end; end')
149
+ method.num_statements.should == 3
150
+ end
151
+
152
+ it 'counts 1 statement in a rescue' do
153
+ method = process_method('def one() begin; callee(); rescue; callee(); end; end')
154
+ method.num_statements.should == 2
155
+ end
156
+
157
+ it 'counts 3 statements in a rescue' do
158
+ method = process_method('def one() begin; callee(); callee(); callee(); rescue; callee(); callee(); callee(); end; end')
159
+ method.num_statements.should == 6
160
+ end
161
+
162
+ it 'counts 1 statement in a when' do
163
+ method = process_method('def one() case fred; when "hi"; callee(); end; end')
164
+ method.num_statements.should == 1
165
+ end
166
+
167
+ it 'counts 3 statements in a when' do
168
+ method = process_method('def one() case fred; when "hi"; callee(); callee(); when "lo"; callee(); end; end')
169
+ method.num_statements.should == 3
170
+ end
171
+
172
+ it 'does not count empty case' do
173
+ method = process_method('def one() case fred; when "hi"; ; when "lo"; ; end; end')
174
+ method.num_statements.should == 0
175
+ end
176
+
177
+ it 'counts else statement' do
178
+ src = <<EOS
179
+ def parse(arg, argv, &error)
180
+ if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
181
+ return nil, block, nil
182
+ end
183
+ opt = (val = parse_arg(val, &error))[1]
184
+ val = conv_arg(*val)
185
+ if opt and !arg
186
+ argv.shift
187
+ else
188
+ val[0] = nil
189
+ end
190
+ val
191
+ end
192
+ EOS
193
+ method = process_method(src)
194
+ method.num_statements.should == 6
195
+ end
196
+ end