reek 3.0.4 → 3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +6 -0
  3. data/README.md +41 -20
  4. data/features/configuration_files/directory_specific_directives.feature +280 -0
  5. data/features/configuration_files/masking_smells.feature +0 -14
  6. data/features/step_definitions/sample_file_steps.rb +2 -2
  7. data/lib/reek/ast/sexp_extensions.rb +72 -60
  8. data/lib/reek/cli/application.rb +2 -5
  9. data/lib/reek/cli/reek_command.rb +1 -1
  10. data/lib/reek/configuration/app_configuration.rb +115 -61
  11. data/lib/reek/configuration/configuration_file_finder.rb +19 -0
  12. data/lib/reek/context/code_context.rb +102 -29
  13. data/lib/reek/context/method_context.rb +11 -48
  14. data/lib/reek/context/module_context.rb +2 -6
  15. data/lib/reek/context/root_context.rb +7 -14
  16. data/lib/reek/examiner.rb +15 -10
  17. data/lib/reek/report/report.rb +5 -4
  18. data/lib/reek/smells/boolean_parameter.rb +1 -1
  19. data/lib/reek/smells/duplicate_method_call.rb +1 -5
  20. data/lib/reek/smells/irresponsible_module.rb +2 -2
  21. data/lib/reek/smells/smell_repository.rb +30 -16
  22. data/lib/reek/smells/utility_function.rb +1 -1
  23. data/lib/reek/source/source_code.rb +10 -6
  24. data/lib/reek/source/source_locator.rb +27 -26
  25. data/lib/reek/spec.rb +8 -6
  26. data/lib/reek/spec/should_reek.rb +6 -2
  27. data/lib/reek/spec/should_reek_of.rb +5 -2
  28. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  29. data/lib/reek/tree_walker.rb +49 -21
  30. data/lib/reek/version.rb +1 -1
  31. data/spec/reek/ast/sexp_extensions_spec.rb +4 -12
  32. data/spec/reek/configuration/app_configuration_spec.rb +80 -52
  33. data/spec/reek/configuration/configuration_file_finder_spec.rb +27 -15
  34. data/spec/reek/context/code_context_spec.rb +66 -17
  35. data/spec/reek/context/method_context_spec.rb +66 -64
  36. data/spec/reek/context/root_context_spec.rb +3 -1
  37. data/spec/reek/context/singleton_method_context_spec.rb +1 -2
  38. data/spec/reek/examiner_spec.rb +5 -8
  39. data/spec/reek/report/xml_report_spec.rb +3 -2
  40. data/spec/reek/smells/attribute_spec.rb +12 -17
  41. data/spec/reek/smells/duplicate_method_call_spec.rb +17 -25
  42. data/spec/reek/smells/feature_envy_spec.rb +4 -5
  43. data/spec/reek/smells/irresponsible_module_spec.rb +43 -0
  44. data/spec/reek/smells/nested_iterators_spec.rb +12 -26
  45. data/spec/reek/smells/too_many_statements_spec.rb +2 -210
  46. data/spec/reek/smells/utility_function_spec.rb +49 -5
  47. data/spec/reek/source/source_locator_spec.rb +39 -43
  48. data/spec/reek/spec/should_reek_of_spec.rb +3 -2
  49. data/spec/reek/spec/should_reek_spec.rb +25 -17
  50. data/spec/reek/tree_walker_spec.rb +214 -23
  51. data/spec/samples/configuration/full_configuration.reek +9 -0
  52. data/spec/spec_helper.rb +10 -10
  53. metadata +4 -3
  54. data/features/configuration_files/overrides_defaults.feature +0 -15
@@ -1,11 +1,12 @@
1
1
  require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/smells/utility_function'
3
+ require_relative '../../../lib/reek/examiner'
3
4
  require_relative 'smell_detector_shared'
4
5
 
5
6
  RSpec.describe Reek::Smells::UtilityFunction do
6
7
  describe 'a detector' do
7
8
  before(:each) do
8
- @source_name = 'dummy_source'
9
+ @source_name = 'string'
9
10
  @detector = build(:smell_detector,
10
11
  smell_type: :UtilityFunction,
11
12
  source: @source_name)
@@ -20,9 +21,7 @@ RSpec.describe Reek::Smells::UtilityFunction do
20
21
  arga.b.c
21
22
  end
22
23
  EOS
23
- source = Reek::Source::SourceCode.from(src)
24
- mctx = Reek::TreeWalker.new.process_def(source.syntax_tree)
25
- @warning = @detector.examine_context(mctx)[0] # SMELL: too cumbersome!
24
+ @warning = Reek::Examiner.new(src, ['UtilityFunction']).smells[0]
26
25
  end
27
26
 
28
27
  it_should_behave_like 'common fields set correctly'
@@ -74,11 +73,56 @@ RSpec.describe Reek::Smells::UtilityFunction do
74
73
  class C
75
74
  def m1(a) a.to_s; end
76
75
  def m2(a) a.to_s; end
77
- module_function :m1, m2
76
+ module_function :m1, :m2
78
77
  end
79
78
  EOS
80
79
  expect(src).not_to reek_of(:UtilityFunction)
81
80
  end
81
+
82
+ it 'does not report module functions defined by earlier modifier' do
83
+ src = <<-EOF
84
+ module M
85
+ module_function
86
+ def simple(a) a.to_s; end
87
+ end
88
+ EOF
89
+ expect(src).not_to reek_of(:UtilityFunction)
90
+ end
91
+
92
+ it 'reports functions preceded by canceled modifier' do
93
+ src = <<-EOF
94
+ module M
95
+ module_function
96
+ public
97
+ def simple(a) a.to_s; end
98
+ end
99
+ EOF
100
+ expect(src).to reek_of(:UtilityFunction)
101
+ end
102
+
103
+ it 'does not report when module_function is called in separate scope' do
104
+ src = <<-EOF
105
+ class C
106
+ def m(a) a.to_s; end
107
+ begin
108
+ module_function :m
109
+ end
110
+ end
111
+ EOF
112
+ expect(src).not_to reek_of(:UtilityFunction)
113
+ end
114
+
115
+ it 'does not report when module_function modifier is called in separate scope' do
116
+ src = <<-EOF
117
+ class C
118
+ begin
119
+ module_function
120
+ end
121
+ def m(a) a.to_s; end
122
+ end
123
+ EOF
124
+ expect(src).not_to reek_of(:UtilityFunction)
125
+ end
82
126
  end
83
127
  end
84
128
 
@@ -1,87 +1,83 @@
1
+ require 'pathname'
1
2
  require_relative '../../spec_helper'
3
+ require_relative '../../../lib/reek/configuration/app_configuration'
2
4
  require_relative '../../../lib/reek/source/source_locator'
3
5
 
4
6
  RSpec.describe Reek::Source::SourceLocator do
5
7
  describe '#sources' do
6
8
  context 'applied to hidden directories' do
7
- let(:path) { 'spec/samples/source_with_hidden_directories' }
8
- let(:expected_files) do
9
- ['spec/samples/source_with_hidden_directories/uncommunicative_parameter_name.rb']
9
+ let(:path) { SAMPLES_PATH.join('source_with_hidden_directories') }
10
+ let(:expected_paths) do
11
+ [SAMPLES_PATH.join('source_with_hidden_directories/uncommunicative_parameter_name.rb')]
10
12
  end
11
- let(:files_that_are_expected_to_be_ignored) do
12
- ['spec/samples/source_with_hidden_directories/.hidden/uncommunicative_method_name.rb']
13
+ let(:paths_that_are_expected_to_be_ignored) do
14
+ [SAMPLES_PATH.join('source_with_hidden_directories/.hidden/\
15
+ uncommunicative_parameter_nameicative_method_name.rb')]
13
16
  end
14
17
 
15
18
  it 'does not scan hidden directories' do
16
19
  sources = described_class.new([path]).sources
17
20
 
18
- expect(sources.map(&:path)).
19
- not_to include(*files_that_are_expected_to_be_ignored)
20
-
21
- expect(sources.map(&:path)).to eq expected_files
21
+ expect(sources).not_to include(*paths_that_are_expected_to_be_ignored)
22
+ expect(sources).to eq expected_paths
22
23
  end
23
24
  end
24
25
 
25
26
  context 'exclude paths' do
26
- let(:config) { 'spec/samples/configuration/with_excluded_paths.reek' }
27
- let(:path) { 'spec/samples/source_with_exclude_paths' }
28
- let(:files_that_are_expected_to_be_ignored) do
27
+ let(:configuration) do
28
+ test_configuration_for(SAMPLES_PATH.join('configuration/with_excluded_paths.reek'))
29
+ end
30
+ let(:path) { SAMPLES_PATH.join('source_with_exclude_paths') }
31
+ let(:paths_that_are_expected_to_be_ignored) do
29
32
  [
30
- 'spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb',
31
- 'spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb'
33
+ SAMPLES_PATH.join('source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb'),
34
+ SAMPLES_PATH.join('source_with_exclude_paths/nested/' \
35
+ 'ignore_me_as_well/irresponsible_module.rb')
32
36
  ]
33
37
  end
34
38
 
35
39
  it 'does not use excluded paths' do
36
- with_test_config(config) do
37
- sources = described_class.new([path]).sources
38
-
39
- expect(sources.map(&:path).sort).
40
- not_to include(*files_that_are_expected_to_be_ignored)
40
+ sources = described_class.new([path], configuration: configuration).sources
41
+ expect(sources).not_to include(*paths_that_are_expected_to_be_ignored)
41
42
 
42
- expect(sources.map(&:path).sort).to eq [
43
- 'spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb'
44
- ]
45
- end
43
+ expect(sources).to eq [
44
+ SAMPLES_PATH.join('source_with_exclude_paths/nested/uncommunicative_parameter_name.rb')
45
+ ]
46
46
  end
47
47
  end
48
48
 
49
- context 'non-ruby files' do
50
- let(:path) { 'spec/samples/source_with_non_ruby_files' }
51
- let(:expected_files) do
52
- ['spec/samples/source_with_non_ruby_files/uncommunicative_parameter_name.rb']
49
+ context 'non-ruby paths' do
50
+ let(:path) { SAMPLES_PATH.join('source_with_non_ruby_files') }
51
+ let(:expected_sources) do
52
+ [SAMPLES_PATH.join('source_with_non_ruby_files/uncommunicative_parameter_name.rb')]
53
53
  end
54
- let(:files_that_are_expected_to_be_ignored) do
54
+ let(:paths_that_are_expected_to_be_ignored) do
55
55
  [
56
- 'spec/samples/source_with_non_ruby_files/gibberish',
57
- 'spec/samples/source_with_non_ruby_files/python_source.py'
56
+ SAMPLES_PATH.join('source_with_non_ruby_files/gibberish'),
57
+ SAMPLES_PATH.join('source_with_non_ruby_files/python_source.py')
58
58
  ]
59
59
  end
60
60
 
61
- it 'does only use ruby source files' do
61
+ it 'does only use ruby source paths' do
62
62
  sources = described_class.new([path]).sources
63
63
 
64
- expect(sources.map(&:path)).
65
- not_to include(*files_that_are_expected_to_be_ignored)
64
+ expect(sources).not_to include(*paths_that_are_expected_to_be_ignored)
66
65
 
67
- expect(sources.map(&:path)).to eq expected_files
66
+ expect(sources).to eq expected_sources
68
67
  end
69
68
  end
70
69
 
71
70
  context 'passing "." or "./" as argument' do
72
- let(:expected_files) do
73
- [
74
- 'spec/spec_helper.rb',
75
- 'lib/reek.rb'
76
- ]
71
+ let(:expected_sources) do
72
+ [Pathname.new('spec/spec_helper.rb'), Pathname.new('lib/reek.rb')]
77
73
  end
78
74
 
79
75
  it 'expands it correctly' do
80
- sources_for_dot = described_class.new(['.']).sources
81
- sources_for_dot_slash = described_class.new(['./']).sources
76
+ sources_for_dot = described_class.new([Pathname.new('.')]).sources
77
+ sources_for_dot_slash = described_class.new([Pathname.new('./')]).sources
82
78
 
83
- expect(sources_for_dot.map(&:path)).to include(*expected_files)
84
- expect(sources_for_dot.map(&:path)).to eq(sources_for_dot_slash.map(&:path))
79
+ expect(sources_for_dot).to include(*expected_sources)
80
+ expect(sources_for_dot).to eq(sources_for_dot_slash)
85
81
  end
86
82
  end
87
83
  end
@@ -1,3 +1,4 @@
1
+ require 'pathname'
1
2
  require_relative '../../spec_helper'
2
3
  require_relative '../../../lib/reek/spec'
3
4
 
@@ -56,8 +57,8 @@ RSpec.describe Reek::Spec::ShouldReekOf do
56
57
 
57
58
  context 'checking code in a File' do
58
59
  before :each do
59
- @clean_file = File.new(Dir['spec/samples/three_clean_files/*.rb'][0])
60
- @smelly_file = File.new(Dir['spec/samples/two_smelly_files/*.rb'][0])
60
+ @clean_file = Pathname.glob("#{SAMPLES_PATH}/three_clean_files/*.rb").first
61
+ @smelly_file = Pathname.glob("#{SAMPLES_PATH}/two_smelly_files/*.rb").first
61
62
  @matcher = Reek::Spec::ShouldReekOf.new(:UncommunicativeVariableName,
62
63
  name: '@s')
63
64
  end
@@ -2,9 +2,8 @@ require_relative '../../spec_helper'
2
2
  require_relative '../../../lib/reek/spec'
3
3
 
4
4
  RSpec.describe Reek::Spec::ShouldReek do
5
- let(:matcher) { Reek::Spec::ShouldReek.new }
6
-
7
5
  describe 'checking code in a string' do
6
+ let(:matcher) { Reek::Spec::ShouldReek.new }
8
7
  let(:clean_code) { 'def good() true; end' }
9
8
  let(:smelly_code) { 'def x() y = 4; end' }
10
9
 
@@ -23,27 +22,36 @@ RSpec.describe Reek::Spec::ShouldReek do
23
22
  end
24
23
 
25
24
  describe 'checking code in a File' do
26
- let(:clean_file) { File.new('spec/samples/three_clean_files/clean_one.rb') }
27
- let(:smelly_file) { File.new('spec/samples/two_smelly_files/dirty_one.rb') }
28
- let(:masked_file) { File.new('spec/samples/clean_due_to_masking/dirty_one.rb') }
25
+ let(:clean_file) { SAMPLES_PATH.join('three_clean_files/clean_one.rb') }
26
+ let(:smelly_file) { SAMPLES_PATH.join('two_smelly_files/dirty_one.rb') }
27
+ let(:masked_file) { SAMPLES_PATH.join('clean_due_to_masking/dirty_one.rb') }
29
28
 
30
- it 'matches a smelly File' do
31
- expect(matcher.matches?(smelly_file)).to be_truthy
32
- end
29
+ context 'matcher without masking' do
30
+ let(:matcher) { Reek::Spec::ShouldReek.new }
33
31
 
34
- it 'doesnt match a fragrant File' do
35
- expect(matcher.matches?(clean_file)).to be_falsey
36
- end
32
+ it 'matches a smelly File' do
33
+ expect(matcher.matches?(smelly_file)).to be_truthy
34
+ end
37
35
 
38
- it 'masks smells using the relevant configuration' do
39
- with_test_config('spec/samples/clean_due_to_masking/masked.reek') do
40
- expect(matcher.matches?(masked_file)).to be_falsey
36
+ it 'doesnt match a fragrant File' do
37
+ expect(matcher.matches?(clean_file)).to be_falsey
38
+ end
39
+
40
+ it 'reports the smells when should_not fails' do
41
+ matcher.matches?(smelly_file)
42
+ expect(matcher.failure_message_when_negated).to match('UncommunicativeVariableName')
41
43
  end
42
44
  end
43
45
 
44
- it 'reports the smells when should_not fails' do
45
- matcher.matches?(smelly_file)
46
- expect(matcher.failure_message_when_negated).to match('UncommunicativeVariableName')
46
+ context 'matcher without masking' do
47
+ let(:path) { SAMPLES_PATH.join('clean_due_to_masking/masked.reek') }
48
+ let(:configuration) { test_configuration_for(path) }
49
+ let(:matcher) { Reek::Spec::ShouldReek.new configuration }
50
+ let(:masked_file) { SAMPLES_PATH.join('clean_due_to_masking/dirty_one.rb') }
51
+
52
+ it 'masks smells using the relevant configuration' do
53
+ expect(matcher.matches?(masked_file)).to be_falsey
54
+ end
47
55
  end
48
56
  end
49
57
  end
@@ -1,34 +1,225 @@
1
1
  require_relative '../spec_helper'
2
2
  require_relative '../../lib/reek/tree_walker'
3
+ require_relative '../../lib/reek/source/source_code'
3
4
 
4
- RSpec.describe Reek::TreeWalker, 'with no method definitions' do
5
- it 'reports no problems for empty source code' do
6
- expect('').not_to reek
7
- end
8
- it 'reports no problems for empty class' do
9
- expect('# clean class for testing purposes
10
- class Fred; end').not_to reek
5
+ # Dummy repository to inject into TreeWalker in order to count statements in
6
+ # all contexts.
7
+ class TestSmellRepository
8
+ attr_reader :num_statements
9
+ def examine(context)
10
+ @num_statements = context.num_statements
11
11
  end
12
12
  end
13
13
 
14
- RSpec.describe Reek::TreeWalker, 'with a global method definition' do
15
- it 'reports no problems for simple method' do
16
- src = 'def Outermost::fred() true; end'
17
- expect(src).not_to reek
18
- end
14
+ def process_method(source)
15
+ exp = Reek::Source::SourceCode.from(source).syntax_tree
16
+ repository = TestSmellRepository.new
17
+ Reek::TreeWalker.new(repository, exp).walk
18
+ repository
19
19
  end
20
20
 
21
- RSpec.describe Reek::TreeWalker, 'when a yield is the receiver' do
22
- it 'reports no problems' do
23
- src = <<EOS
24
- def values(*args)
25
- @to_sql += case
26
- when block_given? then yield.to_sql
27
- else args.to_sql
28
- end
29
- self
21
+ def process_singleton_method(source)
22
+ exp = Reek::Source::SourceCode.from(source).syntax_tree
23
+ repository = TestSmellRepository.new
24
+ Reek::TreeWalker.new(repository, exp).walk
25
+ repository
30
26
  end
31
- EOS
32
- expect(src).not_to reek
27
+
28
+ RSpec.describe Reek::TreeWalker, 'statement counting' do
29
+ it 'counts 1 assignment' do
30
+ method = process_method('def one() val = 4; end')
31
+ expect(method.num_statements).to eq(1)
32
+ end
33
+
34
+ it 'counts 3 assignments' do
35
+ method = process_method('def one() val = 4; val = 4; val = 4; end')
36
+ expect(method.num_statements).to eq(3)
37
+ end
38
+
39
+ it 'counts 1 attr assignment' do
40
+ method = process_method('def one() val[0] = 4; end')
41
+ expect(method.num_statements).to eq(1)
42
+ end
43
+
44
+ it 'counts 1 increment assignment' do
45
+ method = process_method('def one() val += 4; end')
46
+ expect(method.num_statements).to eq(1)
47
+ end
48
+
49
+ it 'counts 1 increment attr assignment' do
50
+ method = process_method('def one() val[0] += 4; end')
51
+ expect(method.num_statements).to eq(1)
52
+ end
53
+
54
+ it 'counts 1 nested assignment' do
55
+ method = process_method('def one() val = fred = 4; end')
56
+ expect(method.num_statements).to eq(1)
57
+ end
58
+
59
+ it 'counts returns' do
60
+ method = process_method('def one() val = 4; true; end')
61
+ expect(method.num_statements).to eq(2)
62
+ end
63
+
64
+ it 'counts nil returns' do
65
+ method = process_method('def one() val = 4; nil; end')
66
+ expect(method.num_statements).to eq(2)
67
+ end
68
+
69
+ context 'with control statements' do
70
+ it 'counts 1 statement in a conditional expression' do
71
+ method = process_method('def one() if val == 4; callee(); end; end')
72
+ expect(method.num_statements).to eq(1)
73
+ end
74
+
75
+ it 'counts 3 statements in a conditional expression' do
76
+ method = process_method('def one() if val == 4; callee(); callee(); callee(); end; end')
77
+ expect(method.num_statements).to eq(3)
78
+ end
79
+
80
+ it 'counts 1 statements in an else' do
81
+ method = process_method('def one() if val == 4; callee(); else; callee(); end; end')
82
+ expect(method.num_statements).to eq(2)
83
+ end
84
+
85
+ it 'counts 3 statements in an else' do
86
+ method = process_method('
87
+ def one()
88
+ if val == 4
89
+ callee(); callee(); callee()
90
+ else
91
+ callee(); callee(); callee()
92
+ end
93
+ end
94
+ ')
95
+ expect(method.num_statements).to eq(6)
96
+ end
97
+
98
+ it 'does not count empty conditional expression' do
99
+ method = process_method('def one() if val == 4; ; end; end')
100
+ expect(method.num_statements).to eq(0)
101
+ end
102
+
103
+ it 'does not count empty else' do
104
+ method = process_method('def one() if val == 4; ; else; ; end; end')
105
+ expect(method.num_statements).to eq(0)
106
+ end
107
+
108
+ it 'counts extra statements in an if condition' do
109
+ method = process_method('def one() if begin val = callee(); val < 4 end; end; end')
110
+ expect(method.num_statements).to eq(1)
111
+ end
112
+
113
+ it 'counts 1 statement in a while loop' do
114
+ method = process_method('def one() while val < 4; callee(); end; end')
115
+ expect(method.num_statements).to eq(1)
116
+ end
117
+
118
+ it 'counts 3 statements in a while loop' do
119
+ source = 'def one() while val < 4; callee(); callee(); callee(); end; end'
120
+ expect(process_method(source).num_statements).to eq(3)
121
+ end
122
+
123
+ it 'counts extra statements in a while condition' do
124
+ method = process_method('def one() while begin val = callee(); val < 4 end; end; end')
125
+ expect(method.num_statements).to eq(1)
126
+ end
127
+
128
+ it 'counts 1 statement in a until loop' do
129
+ method = process_method('def one() until val < 4; callee(); end; end')
130
+ expect(method.num_statements).to eq(1)
131
+ end
132
+
133
+ it 'counts 3 statements in a until loop' do
134
+ source = 'def one() until val < 4; callee(); callee(); callee(); end; end'
135
+ expect(process_method(source).num_statements).to eq(3)
136
+ end
137
+
138
+ it 'counts 1 statement in a for loop' do
139
+ method = process_method('def one() for i in 0..4; callee(); end; end')
140
+ expect(method.num_statements).to eq(1)
141
+ end
142
+
143
+ it 'counts 3 statements in a for loop' do
144
+ source = 'def one() for i in 0..4; callee(); callee(); callee(); end; end'
145
+ expect(process_method(source).num_statements).to eq(3)
146
+ end
147
+
148
+ it 'counts 1 statement in a rescue' do
149
+ method = process_method('def one() begin; callee(); rescue; callee(); end; end')
150
+ expect(method.num_statements).to eq(2)
151
+ end
152
+
153
+ it 'counts 3 statements in a rescue' do
154
+ method = process_method('
155
+ def one()
156
+ begin
157
+ callee(); callee(); callee()
158
+ rescue
159
+ callee(); callee(); callee()
160
+ end
161
+ end
162
+ ')
163
+ expect(method.num_statements).to eq(6)
164
+ end
165
+
166
+ it 'counts 1 statement in a when' do
167
+ method = process_method('def one() case fred; when "hi"; callee(); end; end')
168
+ expect(method.num_statements).to eq(1)
169
+ end
170
+
171
+ it 'counts 3 statements in a when' do
172
+ method = process_method('
173
+ def one()
174
+ case fred
175
+ when "hi" then callee(); callee()
176
+ when "lo" then callee()
177
+ end
178
+ end
179
+ ')
180
+ expect(method.num_statements).to eq(3)
181
+ end
182
+
183
+ it 'counts 1 statement in a case else' do
184
+ source = 'def one() case fred; when "hi"; callee(); else; callee(); end; end'
185
+ expect(process_method(source).num_statements).to eq(2)
186
+ end
187
+
188
+ it 'counts 3 statements in a case else' do
189
+ method = process_method('
190
+ def one()
191
+ case fred
192
+ when "hi" then callee(); callee(); callee()
193
+ else callee(); callee(); callee()
194
+ end
195
+ end
196
+ ')
197
+ expect(method.num_statements).to eq(6)
198
+ end
199
+
200
+ it 'does not count empty case' do
201
+ method = process_method('def one() case fred; when "hi"; ; when "lo"; ; end; end')
202
+ expect(method.num_statements).to eq(0)
203
+ end
204
+
205
+ it 'does not count empty case else' do
206
+ method = process_method('def one() case fred; when "hi"; ; else; ; end; end')
207
+ expect(method.num_statements).to eq(0)
208
+ end
209
+
210
+ it 'counts 2 statement in an iterator' do
211
+ method = process_method('def one() fred.each do; callee(); end; end')
212
+ expect(method.num_statements).to eq(2)
213
+ end
214
+
215
+ it 'counts 4 statements in an iterator' do
216
+ source = 'def one() fred.each do; callee(); callee(); callee(); end; end'
217
+ expect(process_method(source).num_statements).to eq(4)
218
+ end
219
+
220
+ it 'counts 1 statement in a singleton method' do
221
+ method = process_singleton_method('def self.foo; callee(); end')
222
+ expect(method.num_statements).to eq(1)
223
+ end
33
224
  end
34
225
  end