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
@@ -4,34 +4,25 @@ require_lib 'reek/cli/options'
4
4
 
5
5
  RSpec.describe Reek::CLI::Command::TodoListCommand do
6
6
  let(:nil_check) { build :smell_detector, smell_type: :NilCheck }
7
- let(:feature_envy) { build :smell_detector, smell_type: :FeatureEnvy }
8
7
  let(:nested_iterators) { build :smell_detector, smell_type: :NestedIterators }
9
- let(:too_many_statements) { build :smell_detector, smell_type: :TooManyStatements }
10
8
 
11
9
  describe '#execute' do
12
10
  let(:options) { Reek::CLI::Options.new [] }
13
11
  let(:configuration) { instance_double 'Reek::Configuration::AppConfiguration' }
12
+ let(:sources) { [source_file] }
14
13
 
15
14
  let(:command) do
16
15
  described_class.new(options: options,
17
- sources: [],
16
+ sources: sources,
18
17
  configuration: configuration)
19
18
  end
20
19
 
21
20
  before do
22
- $stdout = StringIO.new
23
- allow(File).to receive(:write)
24
- end
25
-
26
- after(:all) do
27
- $stdout = STDOUT
21
+ allow(File).to receive(:write).with(described_class::FILE_NAME, String)
28
22
  end
29
23
 
30
24
  context 'smells found' do
31
- before do
32
- smells = [build(:smell_warning, context: 'Foo#bar')]
33
- allow(command).to receive(:scan_for_smells).and_return(smells)
34
- end
25
+ let(:source_file) { SMELLY_FILE }
35
26
 
36
27
  it 'shows a proper message' do
37
28
  expected = "\n'.todo.reek' generated! You can now use this as a starting point for your configuration.\n"
@@ -39,70 +30,22 @@ RSpec.describe Reek::CLI::Command::TodoListCommand do
39
30
  end
40
31
 
41
32
  it 'returns a success code' do
42
- result = command.execute
43
- expect(result).to eq(Reek::CLI::Options::DEFAULT_SUCCESS_EXIT_CODE)
44
- end
45
-
46
- it 'writes a todo file' do
47
- command.execute
48
- expected_yaml = { 'FeatureEnvy' => { 'exclude' => ['Foo#bar'] } }.to_yaml
49
- expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
50
- end
51
- end
52
-
53
- context 'smells with duplicate context found' do
54
- before do
55
- smells = [
56
- build(:smell_warning, context: 'Foo#bar', smell_detector: feature_envy),
57
- build(:smell_warning, context: 'Foo#bar', smell_detector: feature_envy)
58
- ]
59
- allow(command).to receive(:scan_for_smells).and_return(smells)
33
+ result = Reek::CLI::Silencer.silently { command.execute }
34
+ expect(result).to eq(Reek::CLI::Status::DEFAULT_SUCCESS_EXIT_CODE)
60
35
  end
61
36
 
62
- it 'writes the context into the todo file once' do
63
- command.execute
64
- expected_yaml = { 'FeatureEnvy' => { 'exclude' => ['Foo#bar'] } }.to_yaml
65
- expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
66
- end
67
- end
68
-
69
- context 'smells with default exclusions found' do
70
- let(:smell) { build :smell_warning, smell_detector: too_many_statements, context: 'Foo#bar' }
71
-
72
- before do
73
- allow(command).to receive(:scan_for_smells).and_return [smell]
74
- end
75
-
76
- it 'includes the default exclusions in the generated yaml' do
77
- command.execute
78
- expected_yaml = { 'TooManyStatements' => { 'exclude' => ['initialize', 'Foo#bar'] } }.to_yaml
79
- expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
80
- end
81
- end
82
-
83
- context 'smells of different types found' do
84
- before do
85
- smells = [
86
- build(:smell_warning, context: 'Foo#bar', smell_detector: nil_check),
87
- build(:smell_warning, context: 'Bar#baz', smell_detector: nested_iterators)
88
- ]
89
- allow(command).to receive(:scan_for_smells).and_return(smells)
90
- end
91
-
92
- it 'writes the context into the todo file once' do
93
- command.execute
37
+ it 'writes a todo file with exclusions for each smell' do
38
+ Reek::CLI::Silencer.silently { command.execute }
94
39
  expected_yaml = {
95
- 'NilCheck' => { 'exclude' => ['Foo#bar'] },
96
- 'NestedIterators' => { 'exclude' => ['Bar#baz'] }
40
+ 'UncommunicativeMethodName' => { 'exclude' => ['Smelly#x'] },
41
+ 'UncommunicativeVariableName' => { 'exclude' => ['Smelly#x'] }
97
42
  }.to_yaml
98
43
  expect(File).to have_received(:write).with(described_class::FILE_NAME, expected_yaml)
99
44
  end
100
45
  end
101
46
 
102
47
  context 'no smells found' do
103
- before do
104
- allow(command).to receive(:scan_for_smells).and_return []
105
- end
48
+ let(:source_file) { CLEAN_FILE }
106
49
 
107
50
  it 'shows a proper message' do
108
51
  expected = "\n'.todo.reek' not generated because there were no smells found!\n"
@@ -110,12 +53,12 @@ RSpec.describe Reek::CLI::Command::TodoListCommand do
110
53
  end
111
54
 
112
55
  it 'returns a success code' do
113
- result = command.execute
114
- expect(result).to eq Reek::CLI::Options::DEFAULT_SUCCESS_EXIT_CODE
56
+ result = Reek::CLI::Silencer.silently { command.execute }
57
+ expect(result).to eq Reek::CLI::Status::DEFAULT_SUCCESS_EXIT_CODE
115
58
  end
116
59
 
117
60
  it 'does not write a todo file' do
118
- command.execute
61
+ Reek::CLI::Silencer.silently { command.execute }
119
62
  expect(File).not_to have_received(:write)
120
63
  end
121
64
  end
@@ -32,6 +32,10 @@ RSpec.describe Reek::CLI::Options do
32
32
  allow($stdout).to receive_messages(tty?: false)
33
33
  expect(options.progress_format).to eq :quiet
34
34
  end
35
+
36
+ it 'sets force_exclusion to false by default' do
37
+ expect(options.force_exclusion?).to be false
38
+ end
35
39
  end
36
40
 
37
41
  describe 'parse' do
@@ -142,4 +142,51 @@ RSpec.describe Reek::CodeComment::CodeCommentValidator do
142
142
  end.to raise_error(Reek::Errors::GarbageDetectorConfigurationInCommentError)
143
143
  end
144
144
  end
145
+
146
+ describe 'validating configuration keys' do
147
+ context 'basic options mispelled' do
148
+ it 'raises BadDetectorConfigurationKeyInCommentError' do
149
+ expect do
150
+ # exclude -> exlude and enabled -> nabled
151
+ comment = '# :reek:UncommunicativeMethodName { exlude: alfa, nabled: true }'
152
+ FactoryGirl.build(:code_comment, comment: comment)
153
+ end.to raise_error(Reek::Errors::BadDetectorConfigurationKeyInCommentError)
154
+ end
155
+ end
156
+
157
+ context 'basic options not mispelled' do
158
+ it 'does not raise' do
159
+ expect do
160
+ comment = '# :reek:UncommunicativeMethodName { exclude: alfa, enabled: true }'
161
+ FactoryGirl.build(:code_comment, comment: comment)
162
+ end.not_to raise_error
163
+ end
164
+
165
+ it 'does not raise on regexps' do
166
+ expect do
167
+ comment = '# :reek:UncommunicativeMethodName { exclude: !ruby/regexp /alfa/ }'
168
+ FactoryGirl.build(:code_comment, comment: comment)
169
+ end.not_to raise_error
170
+ end
171
+ end
172
+
173
+ context 'unknown custom options' do
174
+ it 'raises BadDetectorConfigurationKeyInCommentError' do
175
+ expect do
176
+ # max_copies -> mx_copies and min_clump_size -> mn_clump_size
177
+ comment = '# :reek:DataClump { mx_copies: 4, mn_clump_size: 3 }'
178
+ FactoryGirl.build(:code_comment, comment: comment)
179
+ end.to raise_error(Reek::Errors::BadDetectorConfigurationKeyInCommentError)
180
+ end
181
+ end
182
+
183
+ context 'valid custom options' do
184
+ it 'does not raise' do
185
+ expect do
186
+ comment = '# :reek:DataClump { max_copies: 4, min_clump_size: 3 }'
187
+ FactoryGirl.build(:code_comment, comment: comment)
188
+ end.not_to raise_error
189
+ end
190
+ end
191
+ end
145
192
  end
@@ -32,8 +32,9 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
32
32
  end
33
33
 
34
34
  it 'returns the file even if it’s just ‘.reek’' do
35
- found = described_class.find(current: CONFIG_PATH)
36
- expect(found).to eq(CONFIG_PATH.join('.reek'))
35
+ single_configuration_file_dir = CONFIG_PATH.join('single_configuration_file')
36
+ found = described_class.find(current: single_configuration_file_dir)
37
+ expect(found).to eq(single_configuration_file_dir.join('.reek'))
37
38
  end
38
39
 
39
40
  it 'returns the file in home if traversing from the current dir fails' do
@@ -91,25 +92,47 @@ RSpec.describe Reek::Configuration::ConfigurationFileFinder do
91
92
  end
92
93
  end
93
94
 
94
- describe '.load_from_file' do
95
- let(:sample_configuration_loaded) do
96
- {
97
- 'UncommunicativeVariableName' => { 'enabled' => false },
98
- 'UncommunicativeMethodName' => { 'enabled' => false }
99
- }
95
+ context 'more than one configuration file' do
96
+ let(:path) { CONFIG_PATH.join('more_than_one_configuration_file') }
97
+
98
+ it 'prints a message on STDERR' do
99
+ expected_message = "Error: Found multiple configuration files 'regular.reek', 'todo.reek'"
100
+ expect do
101
+ begin
102
+ described_class.find(current: path)
103
+ rescue SystemExit
104
+ end
105
+ end.to output(/#{expected_message}/).to_stderr
100
106
  end
101
107
 
102
- it 'loads the configuration from given file' do
103
- configuration = described_class.load_from_file(CONFIG_PATH.join('full_mask.reek'))
104
- expect(configuration).to eq(sample_configuration_loaded)
108
+ it 'exits' do
109
+ Reek::CLI::Silencer.silently do
110
+ expect do
111
+ described_class.find(current: path)
112
+ end.to raise_error(SystemExit)
113
+ end
105
114
  end
106
115
  end
116
+ end
107
117
 
108
- private
118
+ describe '.load_from_file' do
119
+ let(:sample_configuration_loaded) do
120
+ {
121
+ 'UncommunicativeVariableName' => { 'enabled' => false },
122
+ 'UncommunicativeMethodName' => { 'enabled' => false }
123
+ }
124
+ end
109
125
 
110
- def skip_if_a_config_in_tempdir
111
- found = described_class.find(current: Pathname.new(Dir.tmpdir))
112
- skip "skipped: #{found} exists and would fail this test" if found
126
+ it 'loads the configuration from given file' do
127
+ configuration = described_class.load_from_file(CONFIG_PATH.join('full_mask.reek'))
128
+ expect(configuration).to eq(sample_configuration_loaded)
113
129
  end
114
130
  end
131
+
132
+ private
133
+
134
+ def skip_if_a_config_in_tempdir
135
+ found = described_class.find(current: Pathname.new(Dir.tmpdir))
136
+ skip "skipped: #{found} exists and would fail this test" if found
137
+ end
115
138
  end
@@ -5,7 +5,7 @@ require_lib 'reek/context/module_context'
5
5
  RSpec.describe Reek::Context::CodeContext do
6
6
  context 'name recognition' do
7
7
  let(:ctx) { described_class.new(nil, exp) }
8
- let(:exp) { double('exp') }
8
+ let(:exp) { instance_double('Reek::AST::SexpExtensions::ModuleNode') }
9
9
  let(:exp_name) { 'random_name' }
10
10
  let(:full_name) { "::::::::::::::::::::#{exp_name}" }
11
11
 
@@ -45,7 +45,7 @@ RSpec.describe Reek::Context::CodeContext do
45
45
  context 'when there is an outer' do
46
46
  let(:ctx) { described_class.new(outer, exp) }
47
47
  let(:outer_name) { 'another_random sting' }
48
- let(:outer) { described_class.new(nil, double('exp1')) }
48
+ let(:outer) { described_class.new(nil, instance_double('Reek::AST::Node')) }
49
49
 
50
50
  before do
51
51
  ctx.register_with_parent outer
@@ -167,7 +167,7 @@ RSpec.describe Reek::Context::CodeContext do
167
167
  let(:expression) { Reek::Source::SourceCode.from(src).syntax_tree }
168
168
  let(:outer) { nil }
169
169
  let(:context) { described_class.new(outer, expression) }
170
- let(:sniffer) { double('sniffer') }
170
+ let(:sniffer) { class_double('Reek::SmellDetectors::BaseDetector') }
171
171
 
172
172
  before do
173
173
  context.register_with_parent(outer)
@@ -181,7 +181,7 @@ RSpec.describe Reek::Context::CodeContext do
181
181
  end
182
182
 
183
183
  context 'when there is an outer context' do
184
- let(:outer) { described_class.new(nil, double('exp1')) }
184
+ let(:outer) { described_class.new(nil, instance_double('Reek::AST::Node')) }
185
185
 
186
186
  before do
187
187
  allow(outer).to receive(:config_for).with(sniffer).and_return(
@@ -196,9 +196,9 @@ RSpec.describe Reek::Context::CodeContext do
196
196
  end
197
197
 
198
198
  describe '#register_with_parent' do
199
- let(:context) { described_class.new(nil, double('exp1')) }
200
- let(:first_child) { described_class.new(context, double('exp2')) }
201
- let(:second_child) { described_class.new(context, double('exp3')) }
199
+ let(:context) { described_class.new(nil, instance_double('Reek::AST::Node')) }
200
+ let(:first_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
201
+ let(:second_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
202
202
 
203
203
  it "appends the element to the parent context's list of children" do
204
204
  first_child.register_with_parent context
@@ -209,9 +209,9 @@ RSpec.describe Reek::Context::CodeContext do
209
209
  end
210
210
 
211
211
  describe '#each' do
212
- let(:context) { described_class.new(nil, double('exp1')) }
213
- let(:first_child) { described_class.new(context, double('exp2')) }
214
- let(:second_child) { described_class.new(context, double('exp3')) }
212
+ let(:context) { described_class.new(nil, instance_double('Reek::AST::Node')) }
213
+ let(:first_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
214
+ let(:second_child) { described_class.new(context, instance_double('Reek::AST::Node')) }
215
215
 
216
216
  it 'yields each child' do
217
217
  first_child.register_with_parent context
@@ -5,7 +5,7 @@ RSpec.describe Reek::Context::MethodContext do
5
5
  let(:method_context) { described_class.new(nil, exp) }
6
6
 
7
7
  describe '#matches?' do
8
- let(:exp) { double('exp').as_null_object }
8
+ let(:exp) { instance_double('Reek::AST::SexpExtensions::ModuleNode').as_null_object }
9
9
 
10
10
  before do
11
11
  allow(exp).to receive(:full_name).at_least(:once).and_return('mod')
@@ -8,7 +8,7 @@ RSpec.describe Reek::Context::ModuleContext do
8
8
  module Fred
9
9
  def simple(x) x + 1; end
10
10
  end
11
- ').to reek_of(:UncommunicativeParameterName, name: 'x')
11
+ ').to reek_of(:UncommunicativeParameterName, name: 'x', context: 'Fred#simple')
12
12
  end
13
13
 
14
14
  it 'does not report module with empty class' do
@@ -28,9 +28,13 @@ RSpec.describe Reek::Context::ModuleContext do
28
28
  end
29
29
 
30
30
  describe '#track_visibility' do
31
- let(:context) { described_class.new(nil, double('exp1')) }
32
- let(:first_child) { Reek::Context::MethodContext.new(context, double('exp2', type: :def, name: :foo)) }
33
- let(:second_child) { Reek::Context::MethodContext.new(context, double('exp3', type: :def)) }
31
+ let(:main_exp) { instance_double('Reek::AST::Node') }
32
+ let(:first_def) { instance_double('Reek::AST::SexpExtensions::DefNode', name: :foo) }
33
+ let(:second_def) { instance_double('Reek::AST::SexpExtensions::DefNode') }
34
+
35
+ let(:context) { described_class.new(nil, main_exp) }
36
+ let(:first_child) { Reek::Context::MethodContext.new(context, first_def) }
37
+ let(:second_child) { Reek::Context::MethodContext.new(context, second_def) }
34
38
 
35
39
  it 'sets visibility on subsequent child contexts' do
36
40
  context.append_child_context first_child
@@ -1,8 +1,8 @@
1
- require_relative '../../spec_helper'
1
+ require_relative '../spec_helper'
2
2
  require_lib 'reek/smell_detectors/base_detector'
3
- require_lib 'reek/smell_detectors/detector_repository'
3
+ require_lib 'reek/detector_repository'
4
4
 
5
- RSpec.describe Reek::SmellDetectors::DetectorRepository do
5
+ RSpec.describe Reek::DetectorRepository do
6
6
  describe '.smell_types' do
7
7
  let(:smell_types) { described_class.smell_types }
8
8
 
@@ -28,14 +28,14 @@ RSpec.describe Reek::Examiner do
28
28
  context 'with a fragrant String' do
29
29
  let(:examiner) { described_class.new('def good() true; end') }
30
30
 
31
- it_should_behave_like 'no smells found'
31
+ it_behaves_like 'no smells found'
32
32
  end
33
33
 
34
34
  context 'with a smelly String' do
35
35
  let(:examiner) { described_class.new('def fine() y = 4; end') }
36
36
  let(:expected_first_smell) { 'UncommunicativeVariableName' }
37
37
 
38
- it_should_behave_like 'one smell found'
38
+ it_behaves_like 'one smell found'
39
39
  end
40
40
 
41
41
  context 'with a partially masked smelly File' do
@@ -48,13 +48,13 @@ RSpec.describe Reek::Examiner do
48
48
  let(:path) { CONFIG_PATH.join('partial_mask.reek') }
49
49
  let(:expected_first_smell) { 'UncommunicativeVariableName' }
50
50
 
51
- it_should_behave_like 'one smell found'
51
+ it_behaves_like 'one smell found'
52
52
  end
53
53
 
54
54
  context 'with a fragrant File' do
55
55
  let(:examiner) { described_class.new(CLEAN_FILE) }
56
56
 
57
- it_should_behave_like 'no smells found'
57
+ it_behaves_like 'no smells found'
58
58
  end
59
59
 
60
60
  describe '.new' do
@@ -87,7 +87,7 @@ RSpec.describe Reek::Examiner do
87
87
  examiner = described_class.new code, filter_by_smells: ['DuplicateMethodCall']
88
88
 
89
89
  smell = examiner.smells.first
90
- expect(smell).to be_a(Reek::SmellDetectors::SmellWarning)
90
+ expect(smell).to be_a(Reek::SmellWarning)
91
91
  expect(smell.message).to eq("calls 'bar.call_me()' 2 times")
92
92
  end
93
93
 
@@ -111,41 +111,40 @@ RSpec.describe Reek::Examiner do
111
111
  let(:source) { 'class C; def does_crash_reek; end; end' }
112
112
 
113
113
  let(:examiner) do
114
- detector_repository = instance_double 'Reek::SmellDetectors::DetectorRepository'
114
+ detector_repository = instance_double 'Reek::DetectorRepository'
115
115
  allow(detector_repository).to receive(:examine) do
116
116
  raise ArgumentError, 'Looks like bad source'
117
117
  end
118
- class_double('Reek::SmellDetectors::DetectorRepository').as_stubbed_const
119
- allow(Reek::SmellDetectors::DetectorRepository).to receive(:eligible_smell_types)
120
- allow(Reek::SmellDetectors::DetectorRepository).to receive(:new).and_return detector_repository
118
+ class_double('Reek::DetectorRepository').as_stubbed_const
119
+ allow(Reek::DetectorRepository).to receive(:eligible_smell_types)
120
+ allow(Reek::DetectorRepository).to receive(:new).and_return detector_repository
121
121
 
122
122
  described_class.new source
123
123
  end
124
124
 
125
- it 'returns no smell warnings' do
126
- Reek::CLI::Silencer.silently do
127
- expect(examiner.smells).to be_empty
128
- end
125
+ it 'raises an incomprehensible source error' do
126
+ expect { examiner.smells }.to raise_error Reek::Errors::IncomprehensibleSourceError
129
127
  end
130
128
 
131
- it 'prints the origin' do
129
+ it 'explains the origin of the error' do
132
130
  origin = 'string'
133
- expect { examiner.smells }.to output(/#{origin}/).to_stderr
131
+ expect { examiner.smells }.to raise_error.with_message(/#{origin}/)
134
132
  end
135
133
 
136
134
  it 'explains what to do' do
137
135
  explanation = 'Please double check your Reek configuration'
138
- expect { examiner.smells }.to output(/#{explanation}/).to_stderr
136
+ expect { examiner.smells }.to raise_error.with_message(/#{explanation}/)
139
137
  end
140
138
 
141
- it 'contains a message' do
139
+ it 'contains the original error message' do
142
140
  original = 'Looks like bad source'
143
- expect { examiner.smells }.to output(/#{original}/).to_stderr
141
+ expect { examiner.smells }.to raise_error.with_message(/#{original}/)
144
142
  end
145
143
  end
146
144
  end
147
145
 
148
146
  describe 'bad comment config' do
147
+ let(:examiner) { described_class.new(source) }
149
148
  context 'unknown smell detector' do
150
149
  let(:source) do
151
150
  <<-EOS
@@ -154,21 +153,21 @@ RSpec.describe Reek::Examiner do
154
153
  EOS
155
154
  end
156
155
 
157
- it 'prints out a proper message' do
158
- expected_output = "You are trying to configure an unknown smell detector 'DoesNotExist'"
156
+ it 'raises a bad detector name error' do
157
+ expect { examiner.smells }.to raise_error Reek::Errors::BadDetectorInCommentError
158
+ end
159
+
160
+ it 'explains the reason for the error' do
161
+ message = "You are trying to configure an unknown smell detector 'DoesNotExist'"
159
162
 
160
- expect do
161
- described_class.new(source).smells
162
- end.to output(/#{expected_output}/).to_stderr
163
+ expect { examiner.smells }.to raise_error.with_message(/#{message}/)
163
164
  end
164
165
 
165
- it 'prints out a line and a source' do
166
- expected_output = "The source is 'string' and the comment belongs "\
167
- 'to the expression starting in line 2.'
166
+ it 'explains the origin of the error' do
167
+ details = "The source is 'string' and the comment belongs "\
168
+ 'to the expression starting in line 2.'
168
169
 
169
- expect do
170
- described_class.new(source).smells
171
- end.to output(/#{expected_output}/).to_stderr
170
+ expect { examiner.smells }.to raise_error.with_message(/#{details}/)
172
171
  end
173
172
  end
174
173
 
@@ -180,21 +179,21 @@ RSpec.describe Reek::Examiner do
180
179
  EOS
181
180
  end
182
181
 
183
- it 'prints out a proper message' do
184
- expected_output = "Error: You are trying to configure the smell detector 'UncommunicativeMethodName'"
182
+ it 'raises a garbarge configuration error' do
183
+ expect { examiner.smells }.to raise_error Reek::Errors::GarbageDetectorConfigurationInCommentError
184
+ end
185
+
186
+ it 'explains the reason for the error' do
187
+ message = "Error: You are trying to configure the smell detector 'UncommunicativeMethodName'"
185
188
 
186
- expect do
187
- described_class.new(source).smells
188
- end.to output(/#{expected_output}/).to_stderr
189
+ expect { examiner.smells }.to raise_error.with_message(/#{message}/)
189
190
  end
190
191
 
191
- it 'prints out a line and a source' do
192
- expected_output = "The source is 'string' and "\
193
- 'the comment belongs to the expression starting in line 2'
192
+ it 'explains the origin of the error' do
193
+ details = "The source is 'string' and the comment belongs "\
194
+ 'to the expression starting in line 2.'
194
195
 
195
- expect do
196
- described_class.new(source).smells
197
- end.to output(/#{expected_output}/).to_stderr
196
+ expect { examiner.smells }.to raise_error.with_message(/#{details}/)
198
197
  end
199
198
  end
200
199
  end
@@ -0,0 +1,24 @@
1
+ require_relative '../spec_helper'
2
+ require_lib 'reek/logging_error_handler'
3
+
4
+ RSpec.describe Reek::LoggingErrorHandler do
5
+ describe '#handle' do
6
+ let(:exception) { RuntimeError.new('some message') }
7
+ let(:handler) { described_class.new }
8
+
9
+ it "outputs the exception's message to stderr" do
10
+ expect { handler.handle(exception) }.
11
+ to output(/some message/).to_stderr
12
+ end
13
+
14
+ it 'indicates the exception has been appropriately handled' do
15
+ result = false
16
+
17
+ Reek::CLI::Silencer.silently do
18
+ result = handler.handle(exception)
19
+ end
20
+
21
+ expect(result).to be_truthy
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../../../spec_helper'
2
+ require_lib 'reek/report/code_climate/code_climate_configuration'
3
+
4
+ RSpec.describe Reek::Report::CodeClimateConfiguration do
5
+ yml = described_class.load
6
+ smell_types = Reek::SmellDetectors::BaseDetector.descendants.map do |descendant|
7
+ descendant.name.demodulize
8
+ end
9
+
10
+ smell_types.each do |name|
11
+ config = yml.fetch(name)
12
+ it "provides remediation_points for #{name}" do
13
+ expect(config['remediation_points']).to be_a Integer
14
+ end
15
+
16
+ it "provides content for #{name}" do
17
+ expect(config['content']).to be_a String
18
+ end
19
+ end
20
+
21
+ it 'does not include extraneous configuration' do
22
+ expect(smell_types).to match_array(yml.keys)
23
+ end
24
+ end