reek 1.2.6 → 1.2.8

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 (209) hide show
  1. data/.yardopts +10 -0
  2. data/History.txt +62 -0
  3. data/README.md +74 -0
  4. data/bin/reek +2 -2
  5. data/config/defaults.reek +44 -5
  6. data/features/api.feature +20 -0
  7. data/features/masking_smells.feature +60 -42
  8. data/features/options.feature +7 -2
  9. data/features/rake_task.feature +29 -22
  10. data/features/reports.feature +9 -41
  11. data/features/samples.feature +184 -196
  12. data/features/stdin.feature +5 -8
  13. data/features/step_definitions/reek_steps.rb +8 -4
  14. data/features/support/env.rb +2 -3
  15. data/features/yaml.feature +85 -0
  16. data/lib/reek/cli/application.rb +46 -0
  17. data/lib/reek/cli/command_line.rb +108 -0
  18. data/lib/reek/cli/help_command.rb +18 -0
  19. data/lib/reek/cli/reek_command.rb +37 -0
  20. data/lib/reek/cli/report.rb +58 -0
  21. data/lib/reek/cli/version_command.rb +19 -0
  22. data/lib/reek/cli/yaml_command.rb +32 -0
  23. data/lib/reek/core/code_context.rb +64 -0
  24. data/lib/reek/core/code_parser.rb +166 -0
  25. data/lib/reek/core/method_context.rb +84 -0
  26. data/lib/reek/core/module_context.rb +20 -0
  27. data/lib/reek/core/object_refs.rb +51 -0
  28. data/lib/reek/core/singleton_method_context.rb +20 -0
  29. data/lib/reek/core/smell_configuration.rb +62 -0
  30. data/lib/reek/core/sniffer.rb +105 -0
  31. data/lib/reek/core/stop_context.rb +30 -0
  32. data/lib/reek/core/warning_collector.rb +27 -0
  33. data/lib/reek/examiner.rb +104 -0
  34. data/lib/reek/rake/task.rb +142 -0
  35. data/lib/reek/smell_warning.rb +73 -23
  36. data/lib/reek/smells/attribute.rb +26 -21
  37. data/lib/reek/smells/boolean_parameter.rb +38 -0
  38. data/lib/reek/smells/class_variable.rb +22 -9
  39. data/lib/reek/smells/control_couple.rb +40 -16
  40. data/lib/reek/smells/data_clump.rb +114 -23
  41. data/lib/reek/smells/duplication.rb +55 -15
  42. data/lib/reek/smells/feature_envy.rb +16 -7
  43. data/lib/reek/smells/irresponsible_module.rb +37 -0
  44. data/lib/reek/smells/large_class.rb +37 -19
  45. data/lib/reek/smells/long_method.rb +20 -9
  46. data/lib/reek/smells/long_parameter_list.rb +22 -10
  47. data/lib/reek/smells/long_yield_list.rb +43 -7
  48. data/lib/reek/smells/nested_iterators.rb +68 -8
  49. data/lib/reek/smells/simulated_polymorphism.rb +26 -15
  50. data/lib/reek/smells/smell_detector.rb +29 -51
  51. data/lib/reek/smells/uncommunicative_method_name.rb +74 -0
  52. data/lib/reek/smells/uncommunicative_module_name.rb +74 -0
  53. data/lib/reek/smells/uncommunicative_parameter_name.rb +79 -0
  54. data/lib/reek/smells/uncommunicative_variable_name.rb +89 -0
  55. data/lib/reek/smells/utility_function.rb +51 -12
  56. data/lib/reek/smells.rb +29 -0
  57. data/lib/reek/source/code_comment.rb +37 -0
  58. data/lib/reek/source/config_file.rb +72 -0
  59. data/lib/reek/source/core_extras.rb +46 -0
  60. data/lib/reek/source/reference_collector.rb +28 -0
  61. data/lib/reek/source/sexp_formatter.rb +17 -0
  62. data/lib/reek/source/source_code.rb +44 -0
  63. data/lib/reek/source/source_file.rb +32 -0
  64. data/lib/reek/source/source_locator.rb +42 -0
  65. data/lib/reek/source/tree_dresser.rb +204 -0
  66. data/lib/reek/source.rb +18 -0
  67. data/lib/reek/spec/should_reek.rb +31 -0
  68. data/lib/reek/spec/should_reek_of.rb +37 -0
  69. data/lib/reek/spec/should_reek_only_of.rb +37 -0
  70. data/lib/reek/spec.rb +51 -0
  71. data/lib/reek.rb +8 -4
  72. data/reek.gemspec +9 -7
  73. data/spec/matchers/smell_of_matcher.rb +58 -0
  74. data/spec/reek/{help_command_spec.rb → cli/help_command_spec.rb} +3 -4
  75. data/spec/reek/cli/reek_command_spec.rb +46 -0
  76. data/spec/reek/cli/report_spec.rb +30 -0
  77. data/spec/reek/{version_command_spec.rb → cli/version_command_spec.rb} +3 -3
  78. data/spec/reek/cli/yaml_command_spec.rb +47 -0
  79. data/spec/reek/core/code_context_spec.rb +145 -0
  80. data/spec/reek/{code_parser_spec.rb → core/code_parser_spec.rb} +7 -6
  81. data/spec/reek/{config_spec.rb → core/config_spec.rb} +2 -6
  82. data/spec/reek/{method_context_spec.rb → core/method_context_spec.rb} +16 -36
  83. data/spec/reek/core/module_context_spec.rb +27 -0
  84. data/spec/reek/{object_refs_spec.rb → core/object_refs_spec.rb} +5 -6
  85. data/spec/reek/core/singleton_method_context_spec.rb +9 -0
  86. data/spec/reek/core/smell_configuration_spec.rb +11 -0
  87. data/spec/reek/core/stop_context_spec.rb +17 -0
  88. data/spec/reek/core/warning_collector_spec.rb +27 -0
  89. data/spec/reek/examiner_spec.rb +103 -0
  90. data/spec/reek/smell_warning_spec.rb +74 -82
  91. data/spec/reek/smells/attribute_spec.rb +42 -23
  92. data/spec/reek/smells/behaves_like_variable_detector.rb +2 -2
  93. data/spec/reek/smells/boolean_parameter_spec.rb +66 -0
  94. data/spec/reek/smells/class_variable_spec.rb +60 -75
  95. data/spec/reek/smells/control_couple_spec.rb +37 -32
  96. data/spec/reek/smells/data_clump_spec.rb +63 -24
  97. data/spec/reek/smells/duplication_spec.rb +106 -57
  98. data/spec/reek/smells/feature_envy_spec.rb +113 -92
  99. data/spec/reek/smells/irresponsible_module_spec.rb +58 -0
  100. data/spec/reek/smells/large_class_spec.rb +67 -57
  101. data/spec/reek/smells/long_method_spec.rb +40 -10
  102. data/spec/reek/smells/long_parameter_list_spec.rb +50 -28
  103. data/spec/reek/smells/long_yield_list_spec.rb +57 -0
  104. data/spec/reek/smells/nested_iterators_spec.rb +115 -7
  105. data/spec/reek/smells/simulated_polymorphism_spec.rb +56 -20
  106. data/spec/reek/smells/smell_detector_shared.rb +42 -0
  107. data/spec/reek/smells/uncommunicative_method_name_spec.rb +42 -0
  108. data/spec/reek/smells/uncommunicative_module_name_spec.rb +66 -0
  109. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +71 -0
  110. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +120 -0
  111. data/spec/reek/smells/utility_function_spec.rb +50 -4
  112. data/spec/reek/source/code_comment_spec.rb +82 -0
  113. data/spec/reek/source/object_source_spec.rb +20 -0
  114. data/spec/reek/source/reference_collector_spec.rb +53 -0
  115. data/spec/reek/{adapters/source_spec.rb → source/source_code_spec.rb} +7 -8
  116. data/spec/reek/source/tree_dresser_spec.rb +270 -0
  117. data/spec/reek/spec/should_reek_of_spec.rb +76 -0
  118. data/spec/reek/spec/should_reek_only_of_spec.rb +89 -0
  119. data/spec/reek/{adapters → spec}/should_reek_spec.rb +8 -32
  120. data/spec/samples/all_but_one_masked/clean_one.rb +1 -0
  121. data/spec/samples/all_but_one_masked/dirty.rb +1 -0
  122. data/spec/samples/all_but_one_masked/masked.reek +5 -1
  123. data/spec/samples/clean_due_to_masking/clean_one.rb +1 -0
  124. data/spec/samples/clean_due_to_masking/clean_three.rb +1 -0
  125. data/spec/samples/clean_due_to_masking/clean_two.rb +1 -0
  126. data/spec/samples/clean_due_to_masking/dirty_one.rb +1 -1
  127. data/spec/samples/clean_due_to_masking/dirty_two.rb +1 -1
  128. data/spec/samples/clean_due_to_masking/masked.reek +5 -1
  129. data/spec/samples/config/allow_duplication.reek +3 -0
  130. data/spec/samples/config/deeper_nested_iterators.reek +3 -0
  131. data/spec/samples/corrupt_config_file/dirty.rb +1 -1
  132. data/spec/samples/demo/demo.rb +8 -0
  133. data/spec/samples/empty_config_file/dirty.rb +2 -1
  134. data/spec/samples/exceptions.reek +1 -1
  135. data/spec/samples/inline_config/dirty.rb +16 -0
  136. data/spec/samples/inline_config/masked.reek +7 -0
  137. data/spec/samples/mask_some/dirty.rb +8 -0
  138. data/spec/samples/mask_some/some.reek +8 -0
  139. data/spec/samples/masked/dirty.rb +2 -1
  140. data/spec/samples/masked/masked.reek +3 -1
  141. data/spec/samples/mixed_results/clean_one.rb +1 -0
  142. data/spec/samples/mixed_results/clean_three.rb +1 -0
  143. data/spec/samples/mixed_results/clean_two.rb +1 -0
  144. data/spec/samples/mixed_results/dirty_one.rb +1 -0
  145. data/spec/samples/mixed_results/dirty_two.rb +1 -0
  146. data/spec/samples/not_quite_masked/dirty.rb +2 -1
  147. data/spec/samples/not_quite_masked/masked.reek +1 -1
  148. data/spec/samples/overrides/masked/dirty.rb +2 -1
  149. data/spec/samples/overrides/masked/lower.reek +3 -1
  150. data/spec/samples/three_clean_files/clean_one.rb +1 -0
  151. data/spec/samples/three_clean_files/clean_three.rb +1 -0
  152. data/spec/samples/three_clean_files/clean_two.rb +1 -0
  153. data/spec/samples/two_smelly_files/dirty_one.rb +2 -1
  154. data/spec/samples/two_smelly_files/dirty_two.rb +2 -1
  155. data/spec/spec_helper.rb +10 -2
  156. data/tasks/reek.rake +2 -2
  157. data/tasks/test.rake +12 -3
  158. metadata +121 -79
  159. data/README.rdoc +0 -84
  160. data/features/profile.feature +0 -34
  161. data/lib/reek/adapters/application.rb +0 -46
  162. data/lib/reek/adapters/command_line.rb +0 -77
  163. data/lib/reek/adapters/config_file.rb +0 -31
  164. data/lib/reek/adapters/core_extras.rb +0 -64
  165. data/lib/reek/adapters/rake_task.rb +0 -121
  166. data/lib/reek/adapters/report.rb +0 -86
  167. data/lib/reek/adapters/source.rb +0 -72
  168. data/lib/reek/adapters/spec.rb +0 -133
  169. data/lib/reek/block_context.rb +0 -62
  170. data/lib/reek/class_context.rb +0 -41
  171. data/lib/reek/code_context.rb +0 -68
  172. data/lib/reek/code_parser.rb +0 -203
  173. data/lib/reek/configuration.rb +0 -57
  174. data/lib/reek/detector_stack.rb +0 -37
  175. data/lib/reek/help_command.rb +0 -14
  176. data/lib/reek/if_context.rb +0 -18
  177. data/lib/reek/masking_collection.rb +0 -33
  178. data/lib/reek/method_context.rb +0 -138
  179. data/lib/reek/module_context.rb +0 -49
  180. data/lib/reek/name.rb +0 -57
  181. data/lib/reek/object_refs.rb +0 -49
  182. data/lib/reek/reek_command.rb +0 -28
  183. data/lib/reek/sexp_formatter.rb +0 -10
  184. data/lib/reek/singleton_method_context.rb +0 -26
  185. data/lib/reek/smells/uncommunicative_name.rb +0 -84
  186. data/lib/reek/sniffer.rb +0 -177
  187. data/lib/reek/stop_context.rb +0 -35
  188. data/lib/reek/tree_dresser.rb +0 -82
  189. data/lib/reek/version_command.rb +0 -14
  190. data/lib/reek/yield_call_context.rb +0 -12
  191. data/spec/reek/adapters/report_spec.rb +0 -31
  192. data/spec/reek/adapters/should_reek_of_spec.rb +0 -138
  193. data/spec/reek/adapters/should_reek_only_of_spec.rb +0 -87
  194. data/spec/reek/block_context_spec.rb +0 -65
  195. data/spec/reek/class_context_spec.rb +0 -161
  196. data/spec/reek/code_context_spec.rb +0 -182
  197. data/spec/reek/configuration_spec.rb +0 -12
  198. data/spec/reek/if_context_spec.rb +0 -17
  199. data/spec/reek/masking_collection_spec.rb +0 -236
  200. data/spec/reek/module_context_spec.rb +0 -46
  201. data/spec/reek/name_spec.rb +0 -37
  202. data/spec/reek/object_source_spec.rb +0 -23
  203. data/spec/reek/reek_command_spec.rb +0 -45
  204. data/spec/reek/singleton_method_context_spec.rb +0 -16
  205. data/spec/reek/smells/smell_detector_spec.rb +0 -36
  206. data/spec/reek/smells/uncommunicative_name_spec.rb +0 -146
  207. data/spec/reek/sniffer_spec.rb +0 -11
  208. data/spec/reek/stop_context_spec.rb +0 -33
  209. data/spec/reek/tree_dresser_spec.rb +0 -20
@@ -1,52 +1,57 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
-
3
- require 'reek/smells/control_couple'
1
+ require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'control_couple')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
4
4
 
5
5
  include Reek::Smells
6
6
 
7
7
  describe ControlCouple do
8
+ before(:each) do
9
+ @source_name = 'lets get married'
10
+ @detector = ControlCouple.new(@source_name)
11
+ end
12
+
13
+ it_should_behave_like 'SmellDetector'
14
+
8
15
  context 'conditional on a parameter' do
9
16
  it 'should report a ternary check on a parameter' do
10
- 'def simple(arga) arga ? @ivar : 3 end'.should reek_only_of(:ControlCouple, /arga/)
17
+ src = 'def simple(arga) arga ? @ivar : 3 end'
18
+ src.should smell_of(ControlCouple, ControlCouple::PARAMETER_KEY => 'arga')
11
19
  end
12
20
  it 'should not report a ternary check on an ivar' do
13
- 'def simple(arga) @ivar ? arga : 3 end'.should_not reek
21
+ src = 'def simple(arga) @ivar ? arga : 3 end'
22
+ src.should_not smell_of(ControlCouple)
14
23
  end
15
24
  it 'should not report a ternary check on a lvar' do
16
- 'def simple(arga) lvar = 27; lvar ? arga : @ivar end'.should_not reek
25
+ src = 'def simple(arga) lvar = 27; lvar ? arga : @ivar end'
26
+ src.should_not smell_of(ControlCouple)
17
27
  end
18
28
  it 'should spot a couple inside a block' do
19
- 'def blocks(arg) @text.map { |blk| arg ? blk : "#{blk}" } end'.should reek_of(:ControlCouple, /arg/)
29
+ src = 'def blocks(arg) @text.map { |blk| arg ? blk : "#{blk}" } end'
30
+ src.should smell_of(ControlCouple, ControlCouple::PARAMETER_KEY => 'arg')
20
31
  end
21
32
  end
22
33
 
23
- context 'parameter defaulted with boolean' do
24
- context 'in a method' do
25
- it 'reports a parameter defaulted to true' do
26
- 'def cc(arga = true) end'.should reek_of(:ControlCouple, /arga/)
27
- end
28
- it 'reports a parameter defaulted to false' do
29
- 'def cc(arga = false) end'.should reek_of(:ControlCouple, /arga/)
30
- end
31
- it 'reports two parameters defaulted to booleans' do
32
- src = 'def cc(nowt, arga = true, argb = false, &blk) end'
33
- src.should reek_of(:ControlCouple, /arga/)
34
- src.should reek_of(:ControlCouple, /argb/)
35
- end
34
+ context 'when a smell is reported' do
35
+ before :each do
36
+ src = <<EOS
37
+ def things(arg)
38
+ @text.map do |blk|
39
+ arg ? blk : "blk"
40
+ end
41
+ puts "hello" if arg
42
+ end
43
+ EOS
44
+ ctx = MethodContext.new(nil, src.to_reek_source.syntax_tree)
45
+ smells = @detector.examine(ctx)
46
+ smells.length.should == 1
47
+ @warning = smells[0]
36
48
  end
37
49
 
38
- context 'in a singleton method' do
39
- it 'reports a parameter defaulted to true' do
40
- 'def self.cc(arga = true) end'.should reek_of(:ControlCouple, /arga/)
41
- end
42
- it 'reports a parameter defaulted to false' do
43
- 'def fred.cc(arga = false) end'.should reek_of(:ControlCouple, /arga/)
44
- end
45
- it 'reports two parameters defaulted to booleans' do
46
- src = 'def Module.cc(nowt, arga = true, argb = false, &blk) end'
47
- src.should reek_of(:ControlCouple, /arga/)
48
- src.should reek_of(:ControlCouple, /argb/)
49
- end
50
+ it_should_behave_like 'common fields set correctly'
51
+
52
+ it 'has the correct fields' do
53
+ @warning.smell[ControlCouple::PARAMETER_KEY].should == 'arg'
54
+ @warning.lines.should == [3,6]
50
55
  end
51
56
  end
52
57
  end
@@ -1,32 +1,59 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
-
3
- require 'reek/smells/data_clump'
1
+ require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'data_clump')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
4
4
 
5
5
  include Reek::Smells
6
6
 
7
7
  shared_examples_for 'a data clump detector' do
8
8
  it 'does not report small parameter sets' do
9
9
  src = <<EOS
10
+ # test module
10
11
  #{@context} Scrunch
11
12
  def first(pa) @field == :sym ? 0 : 3; end
12
13
  def second(pa) @field == :sym; end
13
14
  def third(pa) pa - pb + @fred; end
14
15
  end
15
16
  EOS
16
-
17
- src.should_not reek
17
+ src.should_not smell_of(DataClump)
18
18
  end
19
19
 
20
- it 'reports 3 identical pairs in a class' do
21
- src = <<EOS
22
- #{@context} Scrunch
20
+ context 'with 3 identical pairs' do
21
+ before :each do
22
+ @module_name = 'Scrunch'
23
+ @src = <<EOS
24
+ #{@context} #{@module_name}
23
25
  def first(pa, pb) @field == :sym ? 0 : 3; end
24
26
  def second(pa, pb) @field == :sym; end
25
27
  def third(pa, pb) pa - pb + @fred; end
26
28
  end
27
29
  EOS
28
-
29
- src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
30
+ ctx = CodeContext.new(nil, @src.to_reek_source.syntax_tree)
31
+ detector = DataClump.new('newt')
32
+ @smells = detector.examine_context(ctx)
33
+ @warning = @smells[0] # SMELL: too cumbersome!
34
+ @yaml = @warning.to_yaml
35
+ end
36
+ it 'records only the one smell' do
37
+ @smells.length.should == 1
38
+ end
39
+ it 'reports all parameters' do
40
+ @smells[0].smell[DataClump::PARAMETERS_KEY].should == ['pa', 'pb']
41
+ end
42
+ it 'reports the number of occurrences' do
43
+ @smells[0].smell[DataClump::OCCURRENCES_KEY].should == 3
44
+ end
45
+ it 'reports all parameters' do
46
+ @smells[0].smell[DataClump::METHODS_KEY].should == ['first', 'second', 'third']
47
+ end
48
+ it 'reports the declaration line numbers' do
49
+ @smells[0].lines.should == [2,3,4]
50
+ end
51
+ it 'reports the correct smell class' do
52
+ @smells[0].smell_class.should == DataClump::SMELL_CLASS
53
+ end
54
+ it 'reports the context fq name' do
55
+ @smells[0].context.should == @module_name
56
+ end
30
57
  end
31
58
 
32
59
  it 'reports 3 swapped pairs in a class' do
@@ -37,8 +64,8 @@ EOS
37
64
  def tri(pa, pb) pa - pb + @fred; end
38
65
  end
39
66
  EOS
40
-
41
- src.should reek_of(:DataClump, /\[pa, pb\]/, /3 methods/)
67
+ src.should smell_of(DataClump, {DataClump::OCCURRENCES_KEY => 3,
68
+ DataClump::PARAMETERS_KEY => ['pa', 'pb']})
42
69
  end
43
70
 
44
71
  it 'reports 3 identical parameter sets in a class' do
@@ -49,14 +76,11 @@ EOS
49
76
  def third(pa, pb, pc) pa - pb + @fred; end
50
77
  end
51
78
  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/)
79
+ src.should smell_of(DataClump, {DataClump::OCCURRENCES_KEY => 3,
80
+ DataClump::PARAMETERS_KEY => ['pa', 'pb', 'pc']})
57
81
  end
58
82
 
59
- it 'recognises re-ordered identical parameter sets' do
83
+ it 'reports re-ordered identical parameter sets' do
60
84
  src = <<EOS
61
85
  #{@context} Scrunch
62
86
  def first(pb, pa, pc) @field == :sym ? 0 : 3; end
@@ -64,11 +88,8 @@ EOS
64
88
  def third(pa, pb, pc) pa - pb + @fred; end
65
89
  end
66
90
  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/)
91
+ src.should smell_of(DataClump, {DataClump::OCCURRENCES_KEY => 3,
92
+ DataClump::PARAMETERS_KEY => ['pa', 'pb', 'pc']})
72
93
  end
73
94
 
74
95
  it 'counts only identical parameter sets' do
@@ -79,12 +100,30 @@ EOS
79
100
  def fc(name, windowW, windowH) end
80
101
  end
81
102
  EOS
103
+ src.should_not smell_of(DataClump)
104
+ end
82
105
 
83
- src.should_not reek_of(:DataClump)
106
+ it 'gets a real example right' do
107
+ src = <<EOS
108
+ #{@context} Inline
109
+ def generate(src, options) end
110
+ def c (src, options) end
111
+ def c_singleton (src, options) end
112
+ def c_raw (src, options) end
113
+ def c_raw_singleton (src, options) end
114
+ end
115
+ EOS
116
+ src.should smell_of(DataClump, DataClump::OCCURRENCES_KEY => 5)
84
117
  end
85
118
  end
86
119
 
87
120
  describe DataClump do
121
+ before(:each) do
122
+ @detector = DataClump.new('newt')
123
+ end
124
+
125
+ it_should_behave_like 'SmellDetector'
126
+
88
127
  context 'in a class' do
89
128
  before :each do
90
129
  @context = 'class'
@@ -1,77 +1,126 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
-
3
- require 'reek/configuration'
4
- require 'reek/method_context'
5
- require 'reek/stop_context'
6
- require 'reek/smells/duplication'
1
+ require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'duplication')
3
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'core', 'code_parser')
4
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'core', 'sniffer')
5
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
7
6
 
8
7
  include Reek
9
8
  include Reek::Smells
10
9
 
11
- describe Duplication, "repeated method calls" do
12
- it 'should report repeated call' do
13
- 'def double_thing() @other.thing + @other.thing end'.should reek_only_of(:Duplication, /@other.thing/)
14
- end
15
-
16
- it 'should report repeated call to lvar' do
17
- 'def double_thing(other) other[@thing] + other[@thing] end'.should reek_only_of(:Duplication, /other\[@thing\]/)
18
- end
19
-
20
- it 'should report call parameters' do
21
- 'def double_thing() @other.thing(2,3) + @other.thing(2,3) end'.should reek_only_of(:Duplication, /@other.thing\(2, 3\)/)
22
- end
23
-
24
- it 'should report nested calls' do
25
- ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.sniff
26
- ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
27
- ruby.should reek_of(:Duplication, /@other.thing.foo/)
28
- end
10
+ describe Duplication do
29
11
 
30
- it 'should ignore calls to new' do
31
- 'def double_thing() @other.new + @other.new end'.should_not reek
32
- end
12
+ context 'when a smell is reported' do
13
+ before :each do
14
+ @source_name = 'copy-cat'
15
+ @detector = Duplication.new(@source_name)
16
+ src = <<EOS
17
+ def double_thing(other)
18
+ other[@thing]
19
+ not_the_sam(at = all)
20
+ other[@thing]
33
21
  end
22
+ EOS
23
+ ctx = CodeContext.new(nil, src.to_reek_source.syntax_tree)
24
+ smells = @detector.examine_context(ctx)
25
+ smells.length.should == 1
26
+ @warning = smells[0]
27
+ end
34
28
 
35
- describe Duplication, "non-repeated method calls" do
36
- it 'should not report similar calls' do
37
- 'def equals(other) other.thing == self.thing end'.should_not reek
38
- end
29
+ it_should_behave_like 'SmellDetector'
30
+ it_should_behave_like 'common fields set correctly'
39
31
 
40
- it 'should respect call parameters' do
41
- 'def double_thing() @other.thing(3) + @other.thing(2) end'.should_not reek
32
+ it 'reports the call' do
33
+ @warning.smell[Duplication::CALL_KEY].should == 'other[@thing]'
34
+ end
35
+ it 'reports the correct lines' do
36
+ @warning.lines.should == [2,4]
37
+ end
42
38
  end
43
- end
44
39
 
45
- describe Duplication, '#examine' do
46
- before :each do
47
- @mc = MethodContext.new(StopContext.new, s(:defn, :fred))
48
- @dup = Duplication.new
40
+ context "with repeated method calls" do
41
+ it 'reports repeated call' do
42
+ src = 'def double_thing() @other.thing + @other.thing end'
43
+ src.should smell_of(Duplication, Duplication::CALL_KEY => '@other.thing')
44
+ end
45
+ it 'reports repeated call to lvar' do
46
+ src = 'def double_thing(other) other[@thing] + other[@thing] end'
47
+ src.should smell_of(Duplication, Duplication::CALL_KEY => 'other[@thing]')
48
+ end
49
+ it 'reports call parameters' do
50
+ src = 'def double_thing() @other.thing(2,3) + @other.thing(2,3) end'
51
+ src.should smell_of(Duplication, Duplication::CALL_KEY => '@other.thing(2, 3)')
52
+ end
53
+ it 'should report nested calls' do
54
+ src = 'def double_thing() @other.thing.foo + @other.thing.foo end'
55
+ src.should smell_of(Duplication, {Duplication::CALL_KEY => '@other.thing'},
56
+ {Duplication::CALL_KEY => '@other.thing.foo'})
57
+ end
58
+ it 'should ignore calls to new' do
59
+ src = 'def double_thing() @other.new + @other.new end'
60
+ src.should_not smell_of(Duplication)
61
+ end
49
62
  end
50
63
 
51
- it 'should return true when reporting a smell' do
52
- 3.times { @mc.record_call_to(s(:call, nil, :other, s(:arglist)))}
53
- @dup.examine(@mc).should == true
54
- end
55
-
56
- it 'should return false when not reporting a smell' do
57
- @dup.examine(@mc).should == false
64
+ context 'with repeated attribute assignment' do
65
+ it 'reports repeated assignment' do
66
+ src = 'def double_thing(thing) @other[thing] = true; @other[thing] = true; end'
67
+ src.should smell_of(Duplication, Duplication::CALL_KEY => '@other[thing] = true')
68
+ end
69
+ it 'does not report multi-assignments' do
70
+ src = <<EOS
71
+ def _parse ctxt
72
+ ctxt.index, result = @ind, @result
73
+ error, ctxt.index = @err, @err_ind
74
+ end
75
+ EOS
76
+ src.should_not smell_of(Duplication)
77
+ end
58
78
  end
59
79
 
60
- it 'should return false when not reporting calls to new' do
61
- 4.times { @mc.record_call_to(s(:call, s(:Set), :new, s(:arglist)))}
62
- @dup.examine(@mc).should == false
80
+ context "non-repeated method calls" do
81
+ it 'should not report similar calls' do
82
+ src = 'def equals(other) other.thing == self.thing end'
83
+ src.should_not smell_of(Duplication)
84
+ end
85
+ it 'should respect call parameters' do
86
+ src = 'def double_thing() @other.thing(3) + @other.thing(2) end'
87
+ src.should_not smell_of(Duplication)
88
+ end
63
89
  end
64
- end
65
90
 
66
- describe Duplication, 'when disabled' do
67
- before :each do
68
- @ctx = MethodContext.new(StopContext.new, [0, :double_thing])
69
- @dup = Duplication.new({SmellConfiguration::ENABLED_KEY => false})
91
+ context "allowing up to 3 calls" do
92
+ before :each do
93
+ @config = {Duplication::MAX_ALLOWED_CALLS_KEY => 3}
94
+ end
95
+ it 'does not report double calls' do
96
+ src = 'def double_thing() @other.thing + @other.thing end'
97
+ src.should_not smell_of(Duplication).with_config(@config)
98
+ end
99
+ it 'does not report triple calls' do
100
+ src = 'def double_thing() @other.thing + @other.thing + @other.thing end'
101
+ src.should_not smell_of(Duplication).with_config(@config)
102
+ end
103
+ it 'reports quadruple calls' do
104
+ src = 'def double_thing() @other.thing + @other.thing + @other.thing + @other.thing end'
105
+ src.should smell_of(Duplication, {Duplication::CALL_KEY => '@other.thing', Duplication::OCCURRENCES_KEY => 4}).with_config(@config)
106
+ end
70
107
  end
71
108
 
72
- it 'should not report repeated call' do
73
- @ctx.record_call_to([:fred])
74
- @ctx.record_call_to([:fred])
75
- @dup.examine(@ctx).should == false
109
+ context "allowing calls to some methods" do
110
+ before :each do
111
+ @config = {Duplication::ALLOW_CALLS_KEY => ['@some.thing',/puts/]}
112
+ end
113
+ it 'does not report calls to some methods' do
114
+ src = 'def double_some_thing() @some.thing + @some.thing end'
115
+ src.should_not smell_of(Duplication).with_config(@config)
116
+ end
117
+ it 'reports calls to other methods' do
118
+ src = 'def double_other_thing() @other.thing + @other.thing end'
119
+ src.should smell_of(Duplication, {Duplication::CALL_KEY => '@other.thing'}).with_config(@config)
120
+ end
121
+ it 'does not report calls to methods specifed with a regular expression' do
122
+ src = 'def double_puts() puts @other.thing; puts @other.thing end'
123
+ src.should smell_of(Duplication, {Duplication::CALL_KEY => '@other.thing'}).with_config(@config)
124
+ end
76
125
  end
77
126
  end
@@ -1,34 +1,76 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
- require 'reek/smells/feature_envy'
3
- require 'reek/method_context'
4
- require 'reek/stop_context'
1
+ require File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'spec_helper')
2
+ require File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'lib', 'reek', 'smells', 'feature_envy')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_detector_shared')
5
4
 
6
5
  include Reek
7
6
  include Reek::Smells
8
7
 
9
8
  describe FeatureEnvy do
10
- it 'should not report use of self' do
11
- 'def simple() self.to_s + self.to_i end'.should_not reek
9
+ context 'with no smell' do
10
+ it 'should not report use of self' do
11
+ 'def simple() self.to_s + self.to_i end'.should_not reek
12
+ end
13
+ it 'should not report vcall with no argument' do
14
+ 'def simple() func; end'.should_not reek
15
+ end
16
+ it 'should not report vcall with argument' do
17
+ 'def simple(arga) func(17); end'.should_not reek
18
+ end
19
+ it 'should not report single use' do
20
+ 'def no_envy(arga) arga.barg(@item) end'.should_not reek
21
+ end
22
+ it 'should not report return value' do
23
+ 'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
24
+ end
25
+ it 'should ignore global variables' do
26
+ 'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
27
+ end
28
+ it 'should not report class methods' do
29
+ 'def simple() self.class.new.flatten_merge(self) end'.should_not reek
30
+ end
31
+ it 'should not report single use of an ivar' do
32
+ 'def no_envy() @item.to_a end'.should_not reek
33
+ end
34
+ it 'should not report returning an ivar' do
35
+ 'def no_envy() @item.to_a; @item end'.should_not reek
36
+ end
37
+ it 'should not report ivar usage in a parameter' do
38
+ 'def no_envy() @item.price + tax(@item) - savings(@item) end'.should_not reek
39
+ end
40
+ it 'should not report single use of an lvar' do
41
+ 'def no_envy() lv = @item; lv.to_a end'.should_not reek
42
+ end
43
+ it 'should not report returning an lvar' do
44
+ 'def no_envy() lv = @item; lv.to_a; lv end'.should_not reek
45
+ end
46
+ it 'ignores lvar usage in a parameter' do
47
+ 'def no_envy() lv = @item; lv.price + tax(lv) - savings(lv); end'.should_not reek
48
+ end
49
+ it 'ignores multiple ivars' do
50
+ src = <<EOS
51
+ def func
52
+ @other.a
53
+ @other.b
54
+ @nother.c
55
+ @nother.d
12
56
  end
13
-
14
- it 'should not report vcall with no argument' do
15
- 'def simple() func; end'.should_not reek
16
- end
17
-
18
- it 'should not report vcall with argument' do
19
- 'def simple(arga) func(17); end'.should_not reek
20
- end
21
-
22
- it 'should not report single use' do
23
- 'def no_envy(arga) arga.barg(@item) end'.should_not reek
24
- end
25
-
26
- it 'should not report return value' do
27
- 'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
57
+ EOS
58
+ src.should_not reek
59
+ #
60
+ # def other.func(me)
61
+ # a
62
+ # b
63
+ # me.nother_c
64
+ # me.nother_d
65
+ # end
66
+ #
67
+ end
28
68
  end
29
69
 
30
- it 'should report many calls to parameter' do
31
- 'def envy(arga) arga.b(arga) + arga.c(@fred) end'.should reek_only_of(:FeatureEnvy, /arga/)
70
+ context 'with 2 calls to a parameter' do
71
+ it 'reports the smell' do
72
+ 'def envy(arga) arga.b(arga) + arga.c(@fred) end'.should reek_only_of(:FeatureEnvy, /arga/)
73
+ end
32
74
  end
33
75
 
34
76
  it 'should report highest affinity' do
@@ -57,26 +99,6 @@ EOS
57
99
  src.should reek_of(:FeatureEnvy, /fred/)
58
100
  end
59
101
 
60
- it 'should ignore global variables' do
61
- 'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
62
- end
63
-
64
- it 'should not report class methods' do
65
- 'def simple() self.class.new.flatten_merge(self) end'.should_not reek
66
- end
67
-
68
- it 'should not report single use of an ivar' do
69
- 'def no_envy() @item.to_a end'.should_not reek
70
- end
71
-
72
- it 'should not report returning an ivar' do
73
- 'def no_envy() @item.to_a; @item end'.should_not reek
74
- end
75
-
76
- it 'should not report ivar usage in a parameter' do
77
- 'def no_envy() @item.price + tax(@item) - savings(@item) end'.should_not reek
78
- end
79
-
80
102
  it 'should not be fooled by duplication' do
81
103
  'def feed(thing) @cow.feed_to(thing.pig); @duck.feed_to(thing.pig) end'.should reek_only_of(:Duplication, /thing.pig/)
82
104
  end
@@ -85,14 +107,6 @@ EOS
85
107
  'def feed(thing) cow.feed_to(thing.pig); duck.feed_to(thing.pig) end'.should reek_only_of(:Duplication, /thing.pig/)
86
108
  end
87
109
 
88
- it 'should not report single use of an lvar' do
89
- 'def no_envy() lv = @item; lv.to_a end'.should_not reek
90
- end
91
-
92
- it 'should not report returning an lvar' do
93
- 'def no_envy() lv = @item; lv.to_a; lv end'.should_not reek
94
- end
95
-
96
110
  it 'should report many calls to lvar' do
97
111
  'def envy() lv = @item; lv.price + lv.tax; end'.should reek_only_of(:FeatureEnvy, /lv/)
98
112
  #
@@ -105,30 +119,6 @@ EOS
105
119
  # end
106
120
  end
107
121
 
108
- it 'ignores lvar usage in a parameter' do
109
- 'def no_envy() lv = @item; lv.price + tax(lv) - savings(lv); end'.should_not reek
110
- end
111
-
112
- it 'ignores multiple ivars' do
113
- src = <<EOS
114
- def func
115
- @other.a
116
- @other.b
117
- @nother.c
118
- @nother.d
119
- end
120
- EOS
121
- src.should_not reek
122
- #
123
- # def other.func(me)
124
- # a
125
- # b
126
- # me.nother_c
127
- # me.nother_d
128
- # end
129
- #
130
- end
131
-
132
122
  it 'ignores frequent use of a call' do
133
123
  'def func() other.a; other.b; nother.c end'.should_not reek_of(:FeatureEnvy)
134
124
  end
@@ -166,8 +156,8 @@ EOS
166
156
  it 'interprets << correctly' do
167
157
  ruby = <<EOS
168
158
  def report_on(report)
169
- if @is_masked
170
- report.record_masked_smell(self)
159
+ if @is_doubled
160
+ report.record_doubled_smell(self)
171
161
  else
172
162
  report << self
173
163
  end
@@ -178,20 +168,51 @@ EOS
178
168
  end
179
169
  end
180
170
 
181
- describe FeatureEnvy, '#examine' do
182
-
183
- before :each do
184
- @context = MethodContext.new(StopContext.new, s(:defn, :cool))
185
- @fe = FeatureEnvy.new
186
- end
187
-
188
- it 'should return true when reporting a smell' do
189
- @context.refs.record_ref([:lvar, :thing])
190
- @context.refs.record_ref([:lvar, :thing])
191
- @fe.examine(@context).should == true
192
- end
193
-
194
- it 'should return false when not reporting a smell' do
195
- @fe.examine(@context).should == false
171
+ describe FeatureEnvy do
172
+ before(:each) do
173
+ @source_name = 'green as a cucumber'
174
+ @detector = FeatureEnvy.new(@source_name)
175
+ end
176
+
177
+ it_should_behave_like 'SmellDetector'
178
+
179
+ context 'when reporting yaml' do
180
+ before :each do
181
+ @receiver = 'other'
182
+ src = <<EOS
183
+ def envious(other)
184
+ #{@receiver}.call
185
+ self.do_nothing
186
+ #{@receiver}.other
187
+ #{@receiver}.fred
188
+ end
189
+ EOS
190
+ source = src.to_reek_source
191
+ sniffer = Sniffer.new(source)
192
+ @mctx = CodeParser.new(sniffer).process_defn(source.syntax_tree)
193
+ @smells = @detector.examine_context(@mctx)
194
+ end
195
+ it 'reports only that smell' do
196
+ @smells.length.should == 1
197
+ end
198
+ it 'reports the source' do
199
+ @smells[0].source.should == @source_name
200
+ end
201
+ it 'reports the class' do
202
+ @smells[0].smell_class.should == FeatureEnvy::SMELL_CLASS
203
+ end
204
+ it 'reports the subclass' do
205
+ @smells[0].subclass.should == FeatureEnvy::SMELL_SUBCLASS
206
+ end
207
+ it 'reports the envious receiver' do
208
+ @smells[0].smell[FeatureEnvy::RECEIVER_KEY].should == @receiver
209
+ end
210
+ it 'reports the number of references' do
211
+ @smells[0].smell[FeatureEnvy::REFERENCES_KEY].should == 3
212
+ end
213
+ it 'reports the referring lines' do
214
+ pending
215
+ @smells[0].lines.should == [2, 4, 5]
216
+ end
196
217
  end
197
218
  end