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.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -7
- data/.travis.yml +13 -9
- data/CHANGELOG.md +36 -3
- data/Dockerfile +6 -5
- data/Gemfile +8 -7
- data/README.md +45 -27
- data/docs/How-To-Write-New-Detectors.md +6 -6
- data/docs/Irresponsible-Module.md +8 -1
- data/docs/Nil-Check.md +1 -1
- data/docs/Prima-Donna-Method.md +27 -0
- data/docs/RSpec-matchers.md +5 -10
- data/docs/Unused-Private-Method.md +27 -0
- data/docs/templates/default/docstring/setup.rb +1 -8
- data/features/command_line_interface/options.feature +5 -1
- data/features/command_line_interface/stdin.feature +1 -1
- data/features/configuration_files/exclude_paths_directives.feature +43 -0
- data/features/configuration_files/warn_about_multiple_configuration_files.feature +44 -0
- data/features/configuration_via_source_comments/erroneous_source_comments.feature +15 -0
- data/features/samples.feature +3 -8
- data/features/step_definitions/reek_steps.rb +5 -5
- data/features/todo_list.feature +1 -2
- data/lib/reek/ast/reference_collector.rb +2 -4
- data/lib/reek/ast/sexp_extensions/arguments.rb +0 -5
- data/lib/reek/ast/sexp_extensions/constant.rb +1 -0
- data/lib/reek/cli/application.rb +7 -8
- data/lib/reek/cli/command/report_command.rb +3 -1
- data/lib/reek/cli/options.rb +29 -13
- data/lib/reek/cli/status.rb +10 -0
- data/lib/reek/code_comment.rb +48 -6
- data/lib/reek/configuration/configuration_file_finder.rb +87 -27
- data/lib/reek/context/ghost_context.rb +1 -2
- data/lib/reek/context_builder.rb +26 -1
- data/lib/reek/detector_repository.rb +64 -0
- data/lib/reek/errors/bad_detector_configuration_key_in_comment_error.rb +37 -0
- data/lib/reek/errors/bad_detector_in_comment_error.rb +2 -1
- data/lib/reek/errors/base_error.rb +9 -0
- data/lib/reek/errors/garbage_detector_configuration_in_comment_error.rb +2 -1
- data/lib/reek/errors/incomprehensible_source_error.rb +47 -0
- data/lib/reek/errors/parse_error.rb +19 -0
- data/lib/reek/examiner.rb +17 -41
- data/lib/reek/logging_error_handler.rb +15 -0
- data/lib/reek/report/code_climate/code_climate_configuration.rb +12 -0
- data/lib/reek/report/code_climate/code_climate_configuration.yml +156 -0
- data/lib/reek/report/code_climate/code_climate_fingerprint.rb +4 -2
- data/lib/reek/report/code_climate/code_climate_formatter.rb +2 -2
- data/lib/reek/smell_configuration.rb +64 -0
- data/lib/reek/smell_detectors/attribute.rb +0 -2
- data/lib/reek/smell_detectors/base_detector.rb +24 -4
- data/lib/reek/smell_detectors/boolean_parameter.rb +0 -1
- data/lib/reek/smell_detectors/class_variable.rb +0 -1
- data/lib/reek/smell_detectors/control_parameter.rb +0 -1
- data/lib/reek/smell_detectors/data_clump.rb +0 -1
- data/lib/reek/smell_detectors/duplicate_method_call.rb +0 -1
- data/lib/reek/smell_detectors/feature_envy.rb +0 -1
- data/lib/reek/smell_detectors/instance_variable_assumption.rb +0 -1
- data/lib/reek/smell_detectors/irresponsible_module.rb +0 -1
- data/lib/reek/smell_detectors/long_parameter_list.rb +0 -2
- data/lib/reek/smell_detectors/long_yield_list.rb +0 -1
- data/lib/reek/smell_detectors/manual_dispatch.rb +4 -6
- data/lib/reek/smell_detectors/module_initialize.rb +5 -8
- data/lib/reek/smell_detectors/nested_iterators.rb +0 -1
- data/lib/reek/smell_detectors/nil_check.rb +0 -1
- data/lib/reek/smell_detectors/prima_donna_method.rb +30 -1
- data/lib/reek/smell_detectors/repeated_conditional.rb +0 -1
- data/lib/reek/smell_detectors/subclassed_from_core_class.rb +0 -1
- data/lib/reek/smell_detectors/too_many_constants.rb +0 -1
- data/lib/reek/smell_detectors/too_many_instance_variables.rb +0 -1
- data/lib/reek/smell_detectors/too_many_methods.rb +0 -1
- data/lib/reek/smell_detectors/too_many_statements.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_method_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_module_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_parameter_name.rb +0 -1
- data/lib/reek/smell_detectors/uncommunicative_variable_name.rb +0 -1
- data/lib/reek/smell_detectors/unused_parameters.rb +0 -1
- data/lib/reek/smell_detectors/unused_private_method.rb +0 -2
- data/lib/reek/smell_detectors/utility_function.rb +0 -1
- data/lib/reek/smell_warning.rb +85 -0
- data/lib/reek/source/source_code.rb +2 -1
- data/lib/reek/source/source_locator.rb +15 -3
- data/lib/reek/spec/should_reek_of.rb +1 -1
- data/lib/reek/spec.rb +6 -4
- data/lib/reek/version.rb +1 -1
- data/reek.gemspec +1 -1
- data/samples/configuration/more_than_one_configuration_file/todo.reek +0 -0
- data/samples/configuration/single_configuration_file/.reek +0 -0
- data/spec/factories/factories.rb +2 -10
- data/spec/quality/reek_source_spec.rb +5 -3
- data/spec/reek/ast/reference_collector_spec.rb +0 -17
- data/spec/reek/cli/application_spec.rb +25 -1
- data/spec/reek/cli/command/report_command_spec.rb +2 -2
- data/spec/reek/cli/command/todo_list_command_spec.rb +14 -71
- data/spec/reek/cli/options_spec.rb +4 -0
- data/spec/reek/code_comment_spec.rb +47 -0
- data/spec/reek/configuration/configuration_file_finder_spec.rb +38 -15
- data/spec/reek/context/code_context_spec.rb +10 -10
- data/spec/reek/context/method_context_spec.rb +1 -1
- data/spec/reek/context/module_context_spec.rb +8 -4
- data/spec/reek/{smell_detectors/detector_repository_spec.rb → detector_repository_spec.rb} +3 -3
- data/spec/reek/examiner_spec.rb +39 -40
- data/spec/reek/logging_error_handler_spec.rb +24 -0
- data/spec/reek/report/code_climate/code_climate_configuration_spec.rb +24 -0
- data/spec/reek/report/code_climate/code_climate_fingerprint_spec.rb +9 -9
- data/spec/reek/report/yaml_report_spec.rb +4 -4
- data/spec/reek/{smell_detectors/smell_configuration_spec.rb → smell_configuration_spec.rb} +3 -3
- data/spec/reek/smell_detectors/base_detector_spec.rb +18 -0
- data/spec/reek/smell_detectors/duplicate_method_call_spec.rb +2 -2
- data/spec/reek/smell_detectors/feature_envy_spec.rb +18 -8
- data/spec/reek/smell_detectors/manual_dispatch_spec.rb +13 -0
- data/spec/reek/smell_detectors/module_initialize_spec.rb +23 -2
- data/spec/reek/smell_detectors/nested_iterators_spec.rb +1 -1
- data/spec/reek/smell_detectors/prima_donna_method_spec.rb +12 -0
- data/spec/reek/smell_detectors/subclassed_from_core_class_spec.rb +0 -5
- data/spec/reek/smell_detectors/uncommunicative_variable_name_spec.rb +2 -2
- data/spec/reek/smell_detectors/unused_parameters_spec.rb +1 -1
- data/spec/reek/{smell_detectors/smell_warning_spec.rb → smell_warning_spec.rb} +9 -9
- data/spec/reek/source/source_code_spec.rb +6 -29
- data/spec/reek/source/source_locator_spec.rb +48 -16
- data/spec/reek/spec/should_reek_of_spec.rb +1 -1
- data/spec/reek/spec/should_reek_only_of_spec.rb +4 -4
- data/spec/spec_helper.rb +4 -3
- data/tasks/configuration.rake +2 -2
- metadata +26 -14
- data/lib/reek/smell_detectors/detector_repository.rb +0 -66
- data/lib/reek/smell_detectors/smell_configuration.rb +0 -66
- data/lib/reek/smell_detectors/smell_warning.rb +0 -88
- data/tasks/mutant.rake +0 -14
- /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
|
-
|
|
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
|
-
|
|
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::
|
|
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
|
|
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
|
-
'
|
|
96
|
-
'
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
36
|
-
|
|
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
|
-
|
|
95
|
-
let(:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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 '
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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) {
|
|
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,
|
|
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) {
|
|
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,
|
|
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,
|
|
200
|
-
let(:first_child) { described_class.new(context,
|
|
201
|
-
let(:second_child) { described_class.new(context,
|
|
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,
|
|
213
|
-
let(:first_child) { described_class.new(context,
|
|
214
|
-
let(:second_child) { described_class.new(context,
|
|
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) {
|
|
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(:
|
|
32
|
-
let(:
|
|
33
|
-
let(:
|
|
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 '
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
2
|
require_lib 'reek/smell_detectors/base_detector'
|
|
3
|
-
require_lib 'reek/
|
|
3
|
+
require_lib 'reek/detector_repository'
|
|
4
4
|
|
|
5
|
-
RSpec.describe Reek::
|
|
5
|
+
RSpec.describe Reek::DetectorRepository do
|
|
6
6
|
describe '.smell_types' do
|
|
7
7
|
let(:smell_types) { described_class.smell_types }
|
|
8
8
|
|
data/spec/reek/examiner_spec.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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::
|
|
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::
|
|
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::
|
|
119
|
-
allow(Reek::
|
|
120
|
-
allow(Reek::
|
|
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 '
|
|
126
|
-
Reek::
|
|
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 '
|
|
129
|
+
it 'explains the origin of the error' do
|
|
132
130
|
origin = 'string'
|
|
133
|
-
expect { examiner.smells }.to
|
|
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
|
|
136
|
+
expect { examiner.smells }.to raise_error.with_message(/#{explanation}/)
|
|
139
137
|
end
|
|
140
138
|
|
|
141
|
-
it 'contains
|
|
139
|
+
it 'contains the original error message' do
|
|
142
140
|
original = 'Looks like bad source'
|
|
143
|
-
expect { examiner.smells }.to
|
|
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 '
|
|
158
|
-
|
|
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
|
|
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 '
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
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 '
|
|
184
|
-
|
|
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
|
|
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 '
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
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
|