reek 4.5.0 → 4.6.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 (128) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -7
  3. data/.travis.yml +13 -9
  4. data/CHANGELOG.md +36 -3
  5. data/Dockerfile +6 -5
  6. data/Gemfile +8 -7
  7. data/README.md +45 -27
  8. data/docs/How-To-Write-New-Detectors.md +6 -6
  9. data/docs/Irresponsible-Module.md +8 -1
  10. data/docs/Nil-Check.md +1 -1
  11. data/docs/Prima-Donna-Method.md +27 -0
  12. data/docs/RSpec-matchers.md +5 -10
  13. data/docs/Unused-Private-Method.md +27 -0
  14. data/docs/templates/default/docstring/setup.rb +1 -8
  15. data/features/command_line_interface/options.feature +5 -1
  16. data/features/command_line_interface/stdin.feature +1 -1
  17. data/features/configuration_files/exclude_paths_directives.feature +43 -0
  18. data/features/configuration_files/warn_about_multiple_configuration_files.feature +44 -0
  19. data/features/configuration_via_source_comments/erroneous_source_comments.feature +15 -0
  20. data/features/samples.feature +3 -8
  21. data/features/step_definitions/reek_steps.rb +5 -5
  22. data/features/todo_list.feature +1 -2
  23. data/lib/reek/ast/reference_collector.rb +2 -4
  24. data/lib/reek/ast/sexp_extensions/arguments.rb +0 -5
  25. data/lib/reek/ast/sexp_extensions/constant.rb +1 -0
  26. data/lib/reek/cli/application.rb +7 -8
  27. data/lib/reek/cli/command/report_command.rb +3 -1
  28. data/lib/reek/cli/options.rb +29 -13
  29. data/lib/reek/cli/status.rb +10 -0
  30. data/lib/reek/code_comment.rb +48 -6
  31. data/lib/reek/configuration/configuration_file_finder.rb +87 -27
  32. data/lib/reek/context/ghost_context.rb +1 -2
  33. data/lib/reek/context_builder.rb +26 -1
  34. data/lib/reek/detector_repository.rb +64 -0
  35. data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +37 -0
  36. data/lib/reek/errors/bad_detector_in_comment_error.rb +2 -1
  37. data/lib/reek/errors/base_error.rb +9 -0
  38. data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +2 -1
  39. data/lib/reek/errors/incomprehensible_source_error.rb +47 -0
  40. data/lib/reek/errors/parse_error.rb +19 -0
  41. data/lib/reek/examiner.rb +17 -41
  42. data/lib/reek/logging_error_handler.rb +15 -0
  43. data/lib/reek/report/code_climate/code_climate_configuration.rb +12 -0
  44. data/lib/reek/report/code_climate/code_climate_configuration.yml +156 -0
  45. data/lib/reek/report/code_climate/code_climate_fingerprint.rb +4 -2
  46. data/lib/reek/report/code_climate/code_climate_formatter.rb +2 -2
  47. data/lib/reek/smell_configuration.rb +64 -0
  48. data/lib/reek/smell_detectors/attribute.rb +0 -2
  49. data/lib/reek/smell_detectors/base_detector.rb +24 -4
  50. data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
  51. data/lib/reek/smell_detectors/class_variable.rb +0 -1
  52. data/lib/reek/smell_detectors/control_parameter.rb +0 -1
  53. data/lib/reek/smell_detectors/data_clump.rb +0 -1
  54. data/lib/reek/smell_detectors/duplicate_method_call.rb +0 -1
  55. data/lib/reek/smell_detectors/feature_envy.rb +0 -1
  56. data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
  57. data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
  58. data/lib/reek/smell_detectors/long_parameter_list.rb +0 -2
  59. data/lib/reek/smell_detectors/long_yield_list.rb +0 -1
  60. data/lib/reek/smell_detectors/manual_dispatch.rb +4 -6
  61. data/lib/reek/smell_detectors/module_initialize.rb +5 -8
  62. data/lib/reek/smell_detectors/nested_iterators.rb +0 -1
  63. data/lib/reek/smell_detectors/nil_check.rb +0 -1
  64. data/lib/reek/smell_detectors/prima_donna_method.rb +30 -1
  65. data/lib/reek/smell_detectors/repeated_conditional.rb +0 -1
  66. data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
  67. data/lib/reek/smell_detectors/too_many_constants.rb +0 -1
  68. data/lib/reek/smell_detectors/too_many_instance_variables.rb +0 -1
  69. data/lib/reek/smell_detectors/too_many_methods.rb +0 -1
  70. data/lib/reek/smell_detectors/too_many_statements.rb +0 -1
  71. data/lib/reek/smell_detectors/uncommunicative_method_name.rb +0 -1
  72. data/lib/reek/smell_detectors/uncommunicative_module_name.rb +0 -1
  73. data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +0 -1
  74. data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +0 -1
  75. data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
  76. data/lib/reek/smell_detectors/unused_private_method.rb +0 -2
  77. data/lib/reek/smell_detectors/utility_function.rb +0 -1
  78. data/lib/reek/smell_warning.rb +85 -0
  79. data/lib/reek/source/source_code.rb +2 -1
  80. data/lib/reek/source/source_locator.rb +15 -3
  81. data/lib/reek/spec/should_reek_of.rb +1 -1
  82. data/lib/reek/spec.rb +6 -4
  83. data/lib/reek/version.rb +1 -1
  84. data/reek.gemspec +1 -1
  85. data/samples/configuration/more_than_one_configuration_file/todo.reek +0 -0
  86. data/samples/configuration/single_configuration_file/.reek +0 -0
  87. data/spec/factories/factories.rb +2 -10
  88. data/spec/quality/reek_source_spec.rb +5 -3
  89. data/spec/reek/ast/reference_collector_spec.rb +0 -17
  90. data/spec/reek/cli/application_spec.rb +25 -1
  91. data/spec/reek/cli/command/report_command_spec.rb +2 -2
  92. data/spec/reek/cli/command/todo_list_command_spec.rb +14 -71
  93. data/spec/reek/cli/options_spec.rb +4 -0
  94. data/spec/reek/code_comment_spec.rb +47 -0
  95. data/spec/reek/configuration/configuration_file_finder_spec.rb +38 -15
  96. data/spec/reek/context/code_context_spec.rb +10 -10
  97. data/spec/reek/context/method_context_spec.rb +1 -1
  98. data/spec/reek/context/module_context_spec.rb +8 -4
  99. data/spec/reek/{smell_detectors/detector_repository_spec.rb → detector_repository_spec.rb} +3 -3
  100. data/spec/reek/examiner_spec.rb +39 -40
  101. data/spec/reek/logging_error_handler_spec.rb +24 -0
  102. data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +24 -0
  103. data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +9 -9
  104. data/spec/reek/report/yaml_report_spec.rb +4 -4
  105. data/spec/reek/{smell_detectors/smell_configuration_spec.rb → smell_configuration_spec.rb} +3 -3
  106. data/spec/reek/smell_detectors/base_detector_spec.rb +18 -0
  107. data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +2 -2
  108. data/spec/reek/smell_detectors/feature_envy_spec.rb +18 -8
  109. data/spec/reek/smell_detectors/manual_dispatch_spec.rb +13 -0
  110. data/spec/reek/smell_detectors/module_initialize_spec.rb +23 -2
  111. data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
  112. data/spec/reek/smell_detectors/prima_donna_method_spec.rb +12 -0
  113. data/spec/reek/smell_detectors/subclassed_from_core_class_spec.rb +0 -5
  114. data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +2 -2
  115. data/spec/reek/smell_detectors/unused_parameters_spec.rb +1 -1
  116. data/spec/reek/{smell_detectors/smell_warning_spec.rb → smell_warning_spec.rb} +9 -9
  117. data/spec/reek/source/source_code_spec.rb +6 -29
  118. data/spec/reek/source/source_locator_spec.rb +48 -16
  119. data/spec/reek/spec/should_reek_of_spec.rb +1 -1
  120. data/spec/reek/spec/should_reek_only_of_spec.rb +4 -4
  121. data/spec/spec_helper.rb +4 -3
  122. data/tasks/configuration.rake +2 -2
  123. metadata +26 -14
  124. data/lib/reek/smell_detectors/detector_repository.rb +0 -66
  125. data/lib/reek/smell_detectors/smell_configuration.rb +0 -66
  126. data/lib/reek/smell_detectors/smell_warning.rb +0 -88
  127. data/tasks/mutant.rake +0 -14
  128. /data/samples/configuration/{.reek → more_than_one_configuration_file/regular.reek} +0 -0
@@ -16,19 +16,19 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
16
16
  source: 'a/ruby/source/file.rb')
17
17
  end
18
18
 
19
- it 'it computes the fingerprint' do
19
+ it 'computes the fingerprint' do
20
20
  expect(computed).to eq expected_fingerprint
21
21
  end
22
22
  end
23
23
 
24
24
  context 'with code at a specific location' do
25
25
  let(:lines) { [1] }
26
- it_should_behave_like 'computes a fingerprint with no parameters'
26
+ it_behaves_like 'computes a fingerprint with no parameters'
27
27
  end
28
28
 
29
29
  context 'with code at a different location' do
30
30
  let(:lines) { [5] }
31
- it_should_behave_like 'computes a fingerprint with no parameters'
31
+ it_behaves_like 'computes a fingerprint with no parameters'
32
32
  end
33
33
 
34
34
  context 'when the fingerprint should not be computed' do
@@ -41,7 +41,7 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
41
41
  source: 'a/ruby/source/file.rb')
42
42
  end
43
43
 
44
- it 'it returns nil' do
44
+ it 'returns nil' do
45
45
  expect(computed).to be_nil
46
46
  end
47
47
  end
@@ -66,14 +66,14 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
66
66
  let(:name) { 'bravo' }
67
67
  let(:expected_fingerprint) { '9c3fd378178118a67e9509f87cae24f9' }
68
68
 
69
- it_should_behave_like 'computes a fingerprint with identifying parameters'
69
+ it_behaves_like 'computes a fingerprint with identifying parameters'
70
70
  end
71
71
 
72
72
  context 'when the name is another thing it has another fingerprint' do
73
73
  let(:name) { 'echo' }
74
74
  let(:expected_fingerprint) { 'd2a6d2703ce04cca65e7300b7de4b89f' }
75
75
 
76
- it_should_behave_like 'computes a fingerprint with identifying parameters'
76
+ it_behaves_like 'computes a fingerprint with identifying parameters'
77
77
  end
78
78
 
79
79
  shared_examples_for 'computes a fingerprint with identifying and non-identifying parameters' do
@@ -98,7 +98,7 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
98
98
  let(:lines) { [1, 7, 10, 13, 15] }
99
99
  let(:expected_fingerprint) { '238733f4f51ba5473dcbe94a43ec5400' }
100
100
 
101
- it_should_behave_like 'computes a fingerprint with identifying and non-identifying parameters'
101
+ it_behaves_like 'computes a fingerprint with identifying and non-identifying parameters'
102
102
  end
103
103
 
104
104
  context 'when the non-identifying parameters change, it computes the same fingerprint' do
@@ -107,7 +107,7 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
107
107
  let(:lines) { [1, 7, 10, 13, 15, 17, 19, 20, 25] }
108
108
  let(:expected_fingerprint) { '238733f4f51ba5473dcbe94a43ec5400' }
109
109
 
110
- it_should_behave_like 'computes a fingerprint with identifying and non-identifying parameters'
110
+ it_behaves_like 'computes a fingerprint with identifying and non-identifying parameters'
111
111
  end
112
112
 
113
113
  context 'but when the identifying parameters change, it computes a different fingerprint' do
@@ -116,7 +116,7 @@ RSpec.describe Reek::Report::CodeClimateFingerprint do
116
116
  let(:lines) { [1, 7, 10, 13, 15] }
117
117
  let(:expected_fingerprint) { 'e0c35e9223cc19bdb9a04fb3e60573e1' }
118
118
 
119
- it_should_behave_like 'computes a fingerprint with identifying and non-identifying parameters'
119
+ it_behaves_like 'computes a fingerprint with identifying and non-identifying parameters'
120
120
  end
121
121
  end
122
122
  end
@@ -30,8 +30,8 @@ RSpec.describe Reek::Report::YAMLReport do
30
30
  out = StringIO.new
31
31
  instance.show(out)
32
32
  out.rewind
33
- result = YAML.load(out.read)
34
- expected = YAML.load <<-EOS
33
+ result = YAML.safe_load(out.read)
34
+ expected = YAML.safe_load <<-EOS
35
35
  ---
36
36
  - context: "simple"
37
37
  lines:
@@ -57,8 +57,8 @@ RSpec.describe Reek::Report::YAMLReport do
57
57
  out = StringIO.new
58
58
  instance.show(out)
59
59
  out.rewind
60
- result = YAML.load(out.read)
61
- expected = YAML.load <<-EOS
60
+ result = YAML.safe_load(out.read)
61
+ expected = YAML.safe_load <<-EOS
62
62
  ---
63
63
  - context: "simple"
64
64
  lines:
@@ -1,7 +1,7 @@
1
- require_relative '../../spec_helper'
2
- require_lib 'reek/smell_detectors/smell_configuration'
1
+ require_relative '../spec_helper'
2
+ require_lib 'reek/smell_configuration'
3
3
 
4
- RSpec.describe Reek::SmellDetectors::SmellConfiguration do
4
+ RSpec.describe Reek::SmellConfiguration do
5
5
  context 'when overriding default configs' do
6
6
  let(:base_config) do
7
7
  {
@@ -1,5 +1,6 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_lib 'reek/smell_detectors/base_detector'
3
+ require_lib 'reek/smell_detectors/duplicate_method_call'
3
4
 
4
5
  RSpec.describe Reek::SmellDetectors::BaseDetector do
5
6
  describe '.todo_configuration_for' do
@@ -43,4 +44,21 @@ RSpec.describe Reek::SmellDetectors::BaseDetector do
43
44
  expect(described_class.valid_detector?('Unknown')).to be false
44
45
  end
45
46
  end
47
+
48
+ describe '.to_detector' do
49
+ it 'returns the right detector' do
50
+ expect(described_class.to_detector('DuplicateMethodCall')).to eq(Reek::SmellDetectors::DuplicateMethodCall)
51
+ end
52
+
53
+ it 'raise NameError for an invalid detector name' do
54
+ expect { described_class.to_detector('Unknown') }.to raise_error(NameError)
55
+ end
56
+ end
57
+
58
+ describe '.configuration_keys' do
59
+ it 'returns the right keys' do
60
+ expected_keys = Reek::SmellDetectors::DuplicateMethodCall.configuration_keys.to_a
61
+ expect(expected_keys).to eq([:enabled, :exclude, :max_calls, :allow_calls])
62
+ end
63
+ end
46
64
  end
@@ -37,8 +37,8 @@ RSpec.describe Reek::SmellDetectors::DuplicateMethodCall do
37
37
  EOS
38
38
 
39
39
  expect(src).
40
- to reek_of(:DuplicateMethodCall, lines: [3, 4], name: 'charlie.delta', count: 2).
41
- and reek_of(:DuplicateMethodCall, lines: [8, 9], name: 'foxtrot.golf', count: 2)
40
+ to reek_of(:DuplicateMethodCall, lines: [3, 4], name: 'charlie.delta', count: 2).
41
+ and reek_of(:DuplicateMethodCall, lines: [8, 9], name: 'foxtrot.golf', count: 2)
42
42
  end
43
43
 
44
44
  context 'with repeated method calls' do
@@ -33,8 +33,8 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
33
33
  EOS
34
34
 
35
35
  expect(src).
36
- to reek_of(:FeatureEnvy, lines: [3, 3], name: 'charlie').
37
- and reek_of(:FeatureEnvy, lines: [7, 7], name: 'hotel')
36
+ to reek_of(:FeatureEnvy, lines: [3, 3], name: 'charlie').
37
+ and reek_of(:FeatureEnvy, lines: [7, 7], name: 'hotel')
38
38
  end
39
39
 
40
40
  it 'does not report use of self' do
@@ -97,6 +97,16 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
97
97
  expect(src).not_to reek_of(:FeatureEnvy)
98
98
  end
99
99
 
100
+ it 'does not report parameter method called with super' do
101
+ src = 'def alfa(bravo) super(bravo.to_s); end'
102
+ expect(src).not_to reek_of(:FeatureEnvy)
103
+ end
104
+
105
+ it 'reports parameter method called with super and elsewhere' do
106
+ src = 'def alfa(bravo) bravo.charley; super(bravo.to_s); end'
107
+ expect(src).to reek_of(:FeatureEnvy)
108
+ end
109
+
100
110
  it 'ignores multiple ivars' do
101
111
  src = <<-EOS
102
112
  def func
@@ -142,7 +152,7 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
142
152
  and reek_of(:FeatureEnvy, name: 'bravo')
143
153
  end
144
154
 
145
- it 'is not be fooled by duplication' do
155
+ it 'is not fooled by duplication' do
146
156
  src = <<-EOS
147
157
  def alfa(bravo)
148
158
  @charlie.delta(bravo.echo)
@@ -150,18 +160,18 @@ RSpec.describe Reek::SmellDetectors::FeatureEnvy do
150
160
  end
151
161
  EOS
152
162
 
153
- expect(src).to reek_only_of(:DuplicateMethodCall)
163
+ expect(src).to reek_only_of(:DuplicateMethodCall, name: 'bravo.echo')
154
164
  end
155
165
 
156
- it 'does not count local calls' do
166
+ it 'counts local calls' do
157
167
  src = <<-EOS
158
168
  def alfa(bravo)
159
- @charlie.delta(bravo.echo)
160
- @foxtrot.delta(bravo.echo)
169
+ charlie.delta(bravo.echo)
170
+ foxtrot.delta(bravo.echo)
161
171
  end
162
172
  EOS
163
173
 
164
- expect(src).to reek_only_of(:DuplicateMethodCall)
174
+ expect(src).to reek_only_of(:DuplicateMethodCall, name: 'bravo.echo')
165
175
  end
166
176
 
167
177
  it 'reports many calls to lvar' do
@@ -59,4 +59,17 @@ RSpec.describe Reek::SmellDetectors::ManualDispatch do
59
59
 
60
60
  expect(src).to reek_of(:ManualDispatch)
61
61
  end
62
+
63
+ it 'reports occurences in a single method as one smell warning' do
64
+ src = <<-EOS
65
+ class Alfa
66
+ def bravo(charlie, delta)
67
+ return true if charlie.respond_to?(:to_a)
68
+ true if delta.respond_to?(:to_a)
69
+ end
70
+ end
71
+ EOS
72
+
73
+ expect(src).to reek_of(:ManualDispatch, lines: [3, 4], context: 'Alfa#bravo')
74
+ end
62
75
  end
@@ -16,7 +16,17 @@ RSpec.describe Reek::SmellDetectors::ModuleInitialize do
16
16
  source: 'string')
17
17
  end
18
18
 
19
- it 'does not report with method named initialize in a nested class' do
19
+ it 'reports nothing for a method with a different name' do
20
+ src = <<-EOF
21
+ module Alfa
22
+ def bravo; end
23
+ end
24
+ EOF
25
+
26
+ expect(src).not_to reek_of(:ModuleInitialize)
27
+ end
28
+
29
+ it 'reports nothing for a method named initialize in a nested class' do
20
30
  src = <<-EOF
21
31
  module Alfa
22
32
  class Bravo
@@ -28,7 +38,7 @@ RSpec.describe Reek::SmellDetectors::ModuleInitialize do
28
38
  expect(src).not_to reek_of(:ModuleInitialize)
29
39
  end
30
40
 
31
- it 'does not smell with method named initialize in a nested struct' do
41
+ it 'reports nothing for a method named initialize in a nested struct' do
32
42
  src = <<-EOF
33
43
  module Alfa
34
44
  Bravo = Struct.new(:charlie) do
@@ -39,4 +49,15 @@ RSpec.describe Reek::SmellDetectors::ModuleInitialize do
39
49
 
40
50
  expect(src).not_to reek_of(:ModuleInitialize)
41
51
  end
52
+
53
+ it 'can be disabled via comment' do
54
+ src = <<-EOS
55
+ # :reek:ModuleInitialize
56
+ module Alfa
57
+ def initialize; end
58
+ end
59
+ EOS
60
+
61
+ expect(src).not_to reek_of(:ModuleInitialize)
62
+ end
42
63
  end
@@ -155,7 +155,7 @@ RSpec.describe Reek::SmellDetectors::NestedIterators do
155
155
  it 'handles the case where super receives a block and arguments' do
156
156
  src = <<-EOS
157
157
  def alfa
158
- super do |bravo|
158
+ super(delta) do |bravo|
159
159
  bravo.each { |charlie| charlie }
160
160
  end
161
161
  end
@@ -47,4 +47,16 @@ RSpec.describe Reek::SmellDetectors::PrimaDonnaMethod do
47
47
 
48
48
  expect(src).not_to reek_of(:PrimaDonnaMethod)
49
49
  end
50
+
51
+ it 'does not report methods we excluded via comment' do
52
+ source = <<-EOF
53
+ # :reek:PrimaDonnaMethod: { exclude: [ bravo! ] }
54
+ class Alfa
55
+ def bravo!
56
+ end
57
+ end
58
+ EOF
59
+
60
+ expect(source).not_to reek_of(:PrimaDonnaMethod)
61
+ end
50
62
  end
@@ -45,11 +45,6 @@ RSpec.describe Reek::SmellDetectors::SubclassedFromCoreClass do
45
45
  expect(src).not_to reek_of(:SubclassedFromCoreClass)
46
46
  end
47
47
 
48
- it 'reports if we inherit from a core class using Class#new' do
49
- src = 'Alfa = Class.new(Array)'
50
- expect(src).to reek_of(:SubclassedFromCoreClass)
51
- end
52
-
53
48
  it 'reports if inner class inherit from a core class' do
54
49
  src = <<-EOS
55
50
  class Alfa
@@ -25,8 +25,8 @@ RSpec.describe Reek::SmellDetectors::UncommunicativeVariableName do
25
25
  end
26
26
  EOS
27
27
 
28
- expect(src).to reek_of(:UncommunicativeVariableName, lines: [2], name: 'x').
29
- and reek_of(:UncommunicativeVariableName, lines: [3], name: 'y')
28
+ expect(src).to reek_of(:UncommunicativeVariableName, lines: [2], name: 'x').
29
+ and reek_of(:UncommunicativeVariableName, lines: [3], name: 'y')
30
30
  end
31
31
 
32
32
  context 'instance variables' do
@@ -24,7 +24,7 @@ RSpec.describe Reek::SmellDetectors::UnusedParameters do
24
24
 
25
25
  expect(src).
26
26
  to reek_of(:UnusedParameters, lines: [1], name: 'bravo').
27
- and reek_of(:UnusedParameters, lines: [1], name: 'charlie')
27
+ and reek_of(:UnusedParameters, lines: [1], name: 'charlie')
28
28
  end
29
29
 
30
30
  it 'reports nothing for no parameters' do
@@ -1,7 +1,7 @@
1
- require_relative '../../spec_helper'
2
- require_lib 'reek/smell_detectors/smell_warning'
1
+ require_relative '../spec_helper'
2
+ require_lib 'reek/smell_warning'
3
3
 
4
- RSpec.describe Reek::SmellDetectors::SmellWarning do
4
+ RSpec.describe Reek::SmellWarning do
5
5
  let(:duplication_detector) { build(:smell_detector, smell_type: 'DuplicateMethodCall') }
6
6
  let(:feature_envy_detector) { build(:smell_detector, smell_type: 'FeatureEnvy') }
7
7
  let(:utility_function_detector) { build(:smell_detector, smell_type: 'UtilityFunction') }
@@ -30,14 +30,14 @@ RSpec.describe Reek::SmellDetectors::SmellWarning do
30
30
  let(:first) { build(:smell_warning, smell_detector: duplication_detector) }
31
31
  let(:second) { build(:smell_warning, smell_detector: feature_envy_detector) }
32
32
 
33
- it_should_behave_like 'first sorts ahead of second'
33
+ it_behaves_like 'first sorts ahead of second'
34
34
  end
35
35
 
36
36
  context 'smells differing only by lines' do
37
37
  let(:first) { build(:smell_warning, smell_detector: feature_envy_detector, lines: [2]) }
38
38
  let(:second) { build(:smell_warning, smell_detector: feature_envy_detector, lines: [3]) }
39
39
 
40
- it_should_behave_like 'first sorts ahead of second'
40
+ it_behaves_like 'first sorts ahead of second'
41
41
  end
42
42
 
43
43
  context 'smells differing only by context' do
@@ -46,7 +46,7 @@ RSpec.describe Reek::SmellDetectors::SmellWarning do
46
46
  build(:smell_warning, smell_detector: duplication_detector, context: 'second')
47
47
  end
48
48
 
49
- it_should_behave_like 'first sorts ahead of second'
49
+ it_behaves_like 'first sorts ahead of second'
50
50
  end
51
51
 
52
52
  context 'smells differing only by message' do
@@ -59,7 +59,7 @@ RSpec.describe Reek::SmellDetectors::SmellWarning do
59
59
  context: 'ctx', message: 'second message')
60
60
  end
61
61
 
62
- it_should_behave_like 'first sorts ahead of second'
62
+ it_behaves_like 'first sorts ahead of second'
63
63
  end
64
64
 
65
65
  context 'smell name takes precedence over message' do
@@ -70,7 +70,7 @@ RSpec.describe Reek::SmellDetectors::SmellWarning do
70
70
  build(:smell_warning, smell_detector: utility_function_detector, message: 'first message')
71
71
  end
72
72
 
73
- it_should_behave_like 'first sorts ahead of second'
73
+ it_behaves_like 'first sorts ahead of second'
74
74
  end
75
75
 
76
76
  context 'smells differing everywhere' do
@@ -86,7 +86,7 @@ RSpec.describe Reek::SmellDetectors::SmellWarning do
86
86
  message: "has the variable name '@s'")
87
87
  end
88
88
 
89
- it_should_behave_like 'first sorts ahead of second'
89
+ it_behaves_like 'first sorts ahead of second'
90
90
  end
91
91
  end
92
92
 
@@ -26,36 +26,15 @@ RSpec.describe Reek::Source::SourceCode do
26
26
  end
27
27
 
28
28
  context 'when the parser fails' do
29
- let(:catcher) { StringIO.new }
30
29
  let(:source_name) { 'Test source' }
31
30
  let(:error_message) { 'Error message' }
32
31
  let(:parser) { class_double(Parser::Ruby23) }
33
32
  let(:src) { described_class.new(code: '', origin: source_name, parser: parser) }
34
33
 
35
- before { $stderr = catcher }
36
-
37
34
  shared_examples_for 'handling and recording the error' do
38
- it 'does not raise an error' do
39
- src.syntax_tree
40
- end
41
-
42
- it 'returns an empty syntax tree' do
43
- expect(src.syntax_tree).to be_nil
44
- end
45
-
46
- it 'records the syntax error' do
47
- src.syntax_tree
48
- expect(catcher.string).to match(error_class.name)
49
- end
50
-
51
- it 'records the source name' do
52
- src.syntax_tree
53
- expect(catcher.string).to match(source_name)
54
- end
55
-
56
- it 'records the error message' do
57
- src.syntax_tree
58
- expect(catcher.string).to match(error_message)
35
+ it 'raises an informative error' do
36
+ expect { src.syntax_tree }.
37
+ to raise_error(/#{source_name}: #{error_class.name}: #{error_message}/)
59
38
  end
60
39
  end
61
40
 
@@ -68,7 +47,7 @@ RSpec.describe Reek::Source::SourceCode do
68
47
  and_raise error_class.new(diagnostic)
69
48
  end
70
49
 
71
- it_should_behave_like 'handling and recording the error'
50
+ it_behaves_like 'handling and recording the error'
72
51
  end
73
52
 
74
53
  context 'with a Racc::ParseError' do
@@ -79,7 +58,7 @@ RSpec.describe Reek::Source::SourceCode do
79
58
  and_raise(error_class.new(error_message))
80
59
  end
81
60
 
82
- it_should_behave_like 'handling and recording the error'
61
+ it_behaves_like 'handling and recording the error'
83
62
  end
84
63
 
85
64
  context 'with a generic error' do
@@ -91,10 +70,8 @@ RSpec.describe Reek::Source::SourceCode do
91
70
  end
92
71
 
93
72
  it 'raises the error' do
94
- expect { src.syntax_tree }.to raise_error
73
+ expect { src.syntax_tree }.to raise_error error_class
95
74
  end
96
75
  end
97
-
98
- after { $stderr = STDERR }
99
76
  end
100
77
  end
@@ -29,34 +29,66 @@ RSpec.describe Reek::Source::SourceLocator do
29
29
  end
30
30
  end
31
31
 
32
+ # rubocop:disable RSpec/NestedGroups
32
33
  context 'exclude paths' do
33
34
  let(:configuration) do
34
35
  test_configuration_for(CONFIG_PATH.join('with_excluded_paths.reek'))
35
36
  end
36
37
 
37
- let(:path) { SAMPLES_PATH.join('source_with_exclude_paths') }
38
+ let(:options) { instance_double('Reek::CLI::Options', force_exclusion?: false) }
38
39
 
39
- let(:expected_paths) do
40
- [path.join('nested/uncommunicative_parameter_name.rb')]
41
- end
40
+ context 'when the path is a file name in an excluded directory' do
41
+ let(:path) { SAMPLES_PATH.join('source_with_exclude_paths', 'ignore_me', 'uncommunicative_method_name.rb') }
42
42
 
43
- let(:paths_that_are_expected_to_be_ignored) do
44
- [
45
- path.join('ignore_me/uncommunicative_method_name.rb'),
46
- path.join('nested/ignore_me_as_well/irresponsible_module.rb')
47
- ]
48
- end
43
+ context 'when options.force_exclusion? is true' do
44
+ before do
45
+ allow(options).to receive(:force_exclusion?).and_return(true)
46
+ end
49
47
 
50
- it 'does not use excluded paths' do
51
- sources = described_class.new([path], configuration: configuration).sources
52
- expect(sources).not_to include(*paths_that_are_expected_to_be_ignored)
48
+ it 'excludes this file' do
49
+ sources = described_class.new([path], configuration: configuration, options: options).sources
50
+ expect(sources).not_to include(path)
51
+ end
52
+ end
53
+
54
+ context 'when options.force_exclusion? is false' do
55
+ before do
56
+ allow(options).to receive(:force_exclusion?).and_return(false)
57
+ end
58
+
59
+ it 'includes this file' do
60
+ sources = described_class.new([path], configuration: configuration, options: options).sources
61
+ expect(sources).to include(path)
62
+ end
63
+ end
53
64
  end
54
65
 
55
- it 'scans directories that are not excluded' do
56
- sources = described_class.new([path], configuration: configuration).sources
57
- expect(sources).to eq expected_paths
66
+ context 'when path is a directory' do
67
+ let(:path) { SAMPLES_PATH.join('source_with_exclude_paths') }
68
+
69
+ let(:expected_paths) do
70
+ [path.join('nested/uncommunicative_parameter_name.rb')]
71
+ end
72
+
73
+ let(:paths_that_are_expected_to_be_ignored) do
74
+ [
75
+ path.join('ignore_me/uncommunicative_method_name.rb'),
76
+ path.join('nested/ignore_me_as_well/irresponsible_module.rb')
77
+ ]
78
+ end
79
+
80
+ it 'does not use excluded paths' do
81
+ sources = described_class.new([path], configuration: configuration, options: options).sources
82
+ expect(sources).not_to include(*paths_that_are_expected_to_be_ignored)
83
+ end
84
+
85
+ it 'scans directories that are not excluded' do
86
+ sources = described_class.new([path], configuration: configuration).sources
87
+ expect(sources).to eq expected_paths
88
+ end
58
89
  end
59
90
  end
91
+ # rubocop:enable RSpec/NestedGroups
60
92
 
61
93
  context 'non-Ruby paths' do
62
94
  let(:path) { SAMPLES_PATH.join('source_with_non_ruby_files') }
@@ -133,7 +133,7 @@ RSpec.describe Reek::Spec::ShouldReekOf do
133
133
  context 'for a smell that is disabled by default' do
134
134
  before do
135
135
  default_config = Reek::SmellDetectors::UnusedPrivateMethod.default_config
136
- expect(default_config[Reek::SmellDetectors::SmellConfiguration::ENABLED_KEY]).to be_falsy
136
+ expect(default_config[Reek::SmellConfiguration::ENABLED_KEY]).to be_falsy
137
137
  end
138
138
 
139
139
  it 'enables the smell detector to match automatically' do
@@ -36,14 +36,14 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
36
36
  context 'with no smells' do
37
37
  let(:smells) { [] }
38
38
 
39
- it_should_behave_like 'no match'
39
+ it_behaves_like 'no match'
40
40
  end
41
41
 
42
42
  context 'with 1 non-matching smell' do
43
43
  let(:control_couple_detector) { build(:smell_detector, smell_type: 'ControlParameter') }
44
44
  let(:smells) { [build(:smell_warning, smell_detector: control_couple_detector)] }
45
45
 
46
- it_should_behave_like 'no match'
46
+ it_behaves_like 'no match'
47
47
  end
48
48
 
49
49
  context 'with 2 non-matching smells' do
@@ -56,7 +56,7 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
56
56
  ]
57
57
  end
58
58
 
59
- it_should_behave_like 'no match'
59
+ it_behaves_like 'no match'
60
60
  end
61
61
 
62
62
  context 'with 1 non-matching and 1 matching smell' do
@@ -70,7 +70,7 @@ RSpec.describe Reek::Spec::ShouldReekOnlyOf do
70
70
  ]
71
71
  end
72
72
 
73
- it_should_behave_like 'no match'
73
+ it_behaves_like 'no match'
74
74
  end
75
75
 
76
76
  context 'with 1 matching smell' do
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require 'timeout'
2
3
  require_relative '../lib/reek'
3
4
  require_relative '../lib/reek/spec'
4
5
  require_relative '../lib/reek/ast/ast_node_class_map'
@@ -84,9 +85,9 @@ RSpec.configure do |config|
84
85
  mocks.verify_doubled_constant_names = true
85
86
  end
86
87
 
87
- # Avoid infinitely running tests. This is mainly useful when running mutant.
88
- # Set the DEBUG environment variable to something truthy like '1' to disable
89
- # this and allow using pry without specs failing.
88
+ # Avoid infinitely running tests. This is mainly useful when running a
89
+ # mutation tester. Set the DEBUG environment variable to something truthy
90
+ # like '1' to disable this and allow using pry without specs failing.
90
91
  unless ENV['DEBUG']
91
92
  config.around(:each) do |example|
92
93
  Timeout.timeout(5, &example)
@@ -1,11 +1,11 @@
1
- require_relative '../lib/reek/smell_detectors/detector_repository'
1
+ require_relative '../lib/reek/detector_repository'
2
2
  require 'yaml'
3
3
 
4
4
  namespace :configuration do
5
5
  desc 'Updates the default configuration file when smell defaults change'
6
6
  task :update_default_configuration do
7
7
  DEFAULT_SMELL_CONFIGURATION = 'defaults.reek'.freeze
8
- content = Reek::SmellDetectors::DetectorRepository.smell_types.each_with_object({}) do |klass, hash|
8
+ content = Reek::DetectorRepository.smell_types.each_with_object({}) do |klass, hash|
9
9
  hash[klass.smell_type] = klass.default_config
10
10
  end
11
11
  File.open(DEFAULT_SMELL_CONFIGURATION, 'w') { |file| YAML.dump(content, file) }