reek 0.3.1 → 1.0.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.
- data/History.txt +20 -0
- data/README.txt +4 -80
- data/Rakefile +15 -4
- data/bin/reek +10 -16
- data/config/defaults.reek +53 -0
- data/lib/reek.rb +1 -21
- data/lib/reek/block_context.rb +37 -0
- data/lib/reek/class_context.rb +73 -0
- data/lib/reek/code_context.rb +47 -0
- data/lib/reek/code_parser.rb +204 -0
- data/lib/reek/exceptions.reek +13 -0
- data/lib/reek/if_context.rb +25 -0
- data/lib/reek/method_context.rb +85 -0
- data/lib/reek/module_context.rb +34 -0
- data/lib/reek/name.rb +42 -0
- data/lib/reek/object_refs.rb +3 -6
- data/lib/reek/options.rb +60 -40
- data/lib/reek/rake_task.rb +20 -29
- data/lib/reek/report.rb +16 -27
- data/lib/reek/sexp_formatter.rb +52 -0
- data/lib/reek/singleton_method_context.rb +27 -0
- data/lib/reek/smell_warning.rb +49 -0
- data/lib/reek/smells/control_couple.rb +21 -13
- data/lib/reek/smells/duplication.rb +23 -27
- data/lib/reek/smells/feature_envy.rb +18 -25
- data/lib/reek/smells/large_class.rb +32 -17
- data/lib/reek/smells/long_method.rb +24 -16
- data/lib/reek/smells/long_parameter_list.rb +25 -18
- data/lib/reek/smells/long_yield_list.rb +7 -9
- data/lib/reek/smells/nested_iterators.rb +13 -9
- data/lib/reek/smells/smell_detector.rb +66 -0
- data/lib/reek/smells/smells.rb +71 -10
- data/lib/reek/smells/uncommunicative_name.rb +49 -41
- data/lib/reek/smells/utility_function.rb +18 -18
- data/lib/reek/source.rb +116 -0
- data/lib/reek/spec.rb +146 -0
- data/lib/reek/stop_context.rb +62 -0
- data/lib/reek/yield_call_context.rb +14 -0
- data/reek.gemspec +42 -0
- data/spec/integration/reek_source_spec.rb +20 -0
- data/spec/{script_spec.rb → integration/script_spec.rb} +11 -24
- data/spec/reek/class_context_spec.rb +198 -0
- data/spec/reek/code_context_spec.rb +92 -0
- data/spec/reek/code_parser_spec.rb +44 -0
- data/spec/reek/config_spec.rb +42 -0
- data/spec/reek/if_context_spec.rb +17 -0
- data/spec/reek/method_context_spec.rb +52 -0
- data/spec/reek/module_context_spec.rb +38 -0
- data/spec/reek/options_spec.rb +2 -28
- data/spec/reek/report_spec.rb +6 -40
- data/spec/reek/sexp_formatter_spec.rb +31 -0
- data/spec/reek/singleton_method_context_spec.rb +17 -0
- data/spec/reek/smells/control_couple_spec.rb +10 -18
- data/spec/reek/smells/duplication_spec.rb +53 -32
- data/spec/reek/smells/feature_envy_spec.rb +87 -49
- data/spec/reek/smells/large_class_spec.rb +45 -4
- data/spec/reek/smells/long_method_spec.rb +25 -41
- data/spec/reek/smells/long_parameter_list_spec.rb +30 -76
- data/spec/reek/smells/nested_iterators_spec.rb +19 -29
- data/spec/reek/smells/smell_spec.rb +9 -18
- data/spec/reek/smells/uncommunicative_name_spec.rb +88 -53
- data/spec/reek/smells/utility_function_spec.rb +45 -44
- data/spec/samples/inline_spec.rb +40 -0
- data/spec/samples/optparse_spec.rb +100 -0
- data/spec/samples/redcloth_spec.rb +93 -0
- data/spec/spec_helper.rb +3 -1
- data/tasks/reek.rake +1 -10
- data/tasks/rspec.rake +16 -35
- metadata +43 -46
- data/lib/reek/checker.rb +0 -66
- data/lib/reek/class_checker.rb +0 -25
- data/lib/reek/file_checker.rb +0 -20
- data/lib/reek/method_checker.rb +0 -198
- data/lib/reek/printer.rb +0 -154
- data/lib/reek/smells/smell.rb +0 -56
- data/lib/reek/version.rb +0 -9
- data/setup.rb +0 -1585
- data/spec/integration_spec.rb +0 -30
- data/spec/reek/class_checker_spec.rb +0 -48
- data/spec/reek/method_checker_spec.rb +0 -67
- data/spec/reek/printer_spec.rb +0 -30
- data/spec/reek_source_spec.rb +0 -12
- data/spec/samples/inline.reek +0 -27
- data/spec/samples/optparse.reek +0 -79
- data/spec/samples/optparse/date.rb +0 -17
- data/spec/samples/optparse/shellwords.rb +0 -6
- data/spec/samples/optparse/time.rb +0 -10
- data/spec/samples/optparse/uri.rb +0 -6
- data/spec/samples/optparse/version.rb +0 -70
- data/spec/samples/redcloth.reek +0 -65
- data/tasks/samples.rake +0 -17
- data/website/index.html +0 -71
- data/website/index.txt +0 -40
- data/website/javascripts/rounded_corners_lite.inc.js +0 -285
- data/website/stylesheets/screen.css +0 -138
- data/website/template.rhtml +0 -48
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/if_context'
|
4
|
+
|
5
|
+
include Reek
|
6
|
+
|
7
|
+
describe IfContext do
|
8
|
+
it 'should find a class within top-level code' do
|
9
|
+
'unless jim; class Array; end; end'.should_not reek
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should find class within top-level code' do
|
13
|
+
stopctx = StopContext.new
|
14
|
+
ifctx = IfContext.new(stopctx, [:if, [:vcall, :jim]])
|
15
|
+
ifctx.find_module(Name.new(:Array)).should_not be_nil
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/method_context'
|
4
|
+
require 'reek/stop_context'
|
5
|
+
|
6
|
+
include Reek
|
7
|
+
|
8
|
+
describe MethodContext, 'matching' do
|
9
|
+
before :each do
|
10
|
+
@element = MethodContext.new(StopContext.new, [0, :mod])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should recognise itself in a collection of names' do
|
14
|
+
@element.matches?(['banana', 'mod']).should == true
|
15
|
+
@element.matches?(['banana']).should == false
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should recognise itself in a collection of REs' do
|
19
|
+
@element.matches?([/banana/, /mod/]).should == true
|
20
|
+
@element.matches?([/banana/]).should == false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe MethodContext, 'matching fq names' do
|
25
|
+
before :each do
|
26
|
+
element = StopContext.new
|
27
|
+
element = ModuleContext.new(element, [0, :mod])
|
28
|
+
element = ClassContext.new(element, [0, :klass])
|
29
|
+
@element = MethodContext.new(element, [0, :meth])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should recognise itself in a collection of names' do
|
33
|
+
@element.matches?(['banana', 'meth']).should == true
|
34
|
+
@element.matches?(['banana', 'klass#meth']).should == true
|
35
|
+
@element.matches?(['banana']).should == false
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should recognise itself in a collection of names' do
|
39
|
+
@element.matches?([/banana/, /meth/]).should == true
|
40
|
+
@element.matches?([/banana/, /klass#meth/]).should == true
|
41
|
+
@element.matches?([/banana/]).should == false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe MethodContext do
|
46
|
+
it 'should record ivars as refs to self' do
|
47
|
+
mctx = MethodContext.new(StopContext.new, [:defn, :feed])
|
48
|
+
mctx.envious_receivers.should == []
|
49
|
+
mctx.record_call_to([:call, [:ivar, :@cow], :feed_to])
|
50
|
+
mctx.envious_receivers.should == []
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
require 'reek/module_context'
|
3
|
+
require 'reek/stop_context'
|
4
|
+
|
5
|
+
include Reek
|
6
|
+
|
7
|
+
describe ModuleContext do
|
8
|
+
it 'should report module name for smell in method' do
|
9
|
+
'module Fred; def simple(x) true; end; end'.should reek_of(:UncommunicativeName, /x/, /simple/, /Fred/)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should not report module with empty class' do
|
13
|
+
'module Fred; class Jim; end; end'.should_not reek
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should handle module with empty class' do
|
17
|
+
stop = StopContext.new
|
18
|
+
modctx = ModuleContext.create(stop, [:module, :Fred, []])
|
19
|
+
modctx.find_module(Name.new(:Jim)).should be_nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe ModuleContext do
|
24
|
+
it 'should recognise global constant' do
|
25
|
+
'module ::Global class Inside; end; end'.should_not reek
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should not find missing global constant' do
|
29
|
+
element = ModuleContext.create(StopContext.new, [:module, [:colon3, :Global], nil])
|
30
|
+
element.myself.should be_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should find global constant' do
|
34
|
+
module ::GlobalTestModule; end
|
35
|
+
element = ModuleContext.create(StopContext.new, [:module, [:colon3, :GlobalTestModule], nil])
|
36
|
+
element.myself.name.should == 'GlobalTestModule'
|
37
|
+
end
|
38
|
+
end
|
data/spec/reek/options_spec.rb
CHANGED
@@ -6,34 +6,8 @@ include Reek
|
|
6
6
|
|
7
7
|
describe Options, ' when given no arguments' do
|
8
8
|
it "should retain the default sort order" do
|
9
|
-
default_order = Options[:
|
9
|
+
default_order = Options[:format]
|
10
10
|
Options.parse ['nosuchfile.rb']
|
11
|
-
Options[:
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
describe Options, ' when --sort_order is specified' do
|
16
|
-
before :each do
|
17
|
-
@default_order = Options[:sort_order]
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'should require an argument' do
|
21
|
-
lambda { Options.parse_args(['-s']) }.should raise_error(OptionParser::MissingArgument)
|
22
|
-
Options[:sort_order].should == @default_order
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should allow sort by smell" do
|
26
|
-
Options.parse %w{-s smell xx}
|
27
|
-
Options[:sort_order].should == Report::SORT_ORDERS[:smell]
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should allow sort by context" do
|
31
|
-
Options.parse %w{-s context xx}
|
32
|
-
Options[:sort_order].should == Report::SORT_ORDERS[:context]
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should reject illegal sort order" do
|
36
|
-
lambda { Options.parse_args(%w{-s tarts}) }.should raise_error(OptionParser::InvalidArgument)
|
37
|
-
Options[:sort_order].should == @default_order
|
11
|
+
Options[:format].should == default_order
|
38
12
|
end
|
39
13
|
end
|
data/spec/reek/report_spec.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
2
|
|
3
|
-
require 'reek/
|
4
|
-
require 'reek/smells'
|
3
|
+
require 'reek/smells/smell_detector'
|
5
4
|
require 'reek/report'
|
5
|
+
require 'reek/source'
|
6
|
+
require 'reek/smells/feature_envy'
|
6
7
|
|
7
8
|
include Reek
|
8
9
|
|
@@ -22,9 +23,7 @@ end
|
|
22
23
|
|
23
24
|
describe Report, "to_s" do
|
24
25
|
before(:each) do
|
25
|
-
rpt =
|
26
|
-
chk = MethodChecker.new(rpt, 'Thing')
|
27
|
-
chk.check_source('def simple(a) a[3] end')
|
26
|
+
rpt = Source.from_s('def simple(a) a[3] end').report
|
28
27
|
@report = rpt.to_s.split("\n")
|
29
28
|
end
|
30
29
|
|
@@ -41,42 +40,9 @@ end
|
|
41
40
|
describe Report, " as a SortedSet" do
|
42
41
|
it 'should only add a smell once' do
|
43
42
|
rpt = Report.new
|
44
|
-
rpt <<
|
43
|
+
rpt << SmellWarning.new(Smells::FeatureEnvy.new, "self", 'too many!')
|
45
44
|
rpt.length.should == 1
|
46
|
-
rpt <<
|
45
|
+
rpt << SmellWarning.new(Smells::FeatureEnvy.new, "self", 'too many!')
|
47
46
|
rpt.length.should == 1
|
48
47
|
end
|
49
48
|
end
|
50
|
-
|
51
|
-
describe SortByContext do
|
52
|
-
before :each do
|
53
|
-
@sorter = SortByContext
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'should return 0 for identical smells' do
|
57
|
-
smell = LongMethod.new('Class#method', 30)
|
58
|
-
@sorter.compare(smell, smell).should == 0
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'should return non-0 for different smells' do
|
62
|
-
@sorter.compare(LongMethod.new('x', 30), LargeClass.new('y')).should == -1
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe SortBySmell do
|
67
|
-
before :each do
|
68
|
-
@sorter = SortBySmell
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'should return 0 for identical smells' do
|
72
|
-
@sorter.compare(LongMethod.new('x', 30), LongMethod.new('x', 30)).should == 0
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'should differentiate identical smells with different contexts' do
|
76
|
-
@sorter.compare(LongMethod.new('x', 29), LongMethod.new('y', 29)).should == -1
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'should differentiate different smells with identical contexts' do
|
80
|
-
@sorter.compare(LongMethod.new('x', 28), LargeClass.new('x')).should == 1
|
81
|
-
end
|
82
|
-
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/code_parser'
|
4
|
+
require 'reek/sexp_formatter'
|
5
|
+
|
6
|
+
include Reek
|
7
|
+
|
8
|
+
def should_print(example)
|
9
|
+
it "should format #{example} correctly" do
|
10
|
+
sexp = CodeParser.parse_tree_for(example)[0]
|
11
|
+
SexpFormatter.format(sexp).should == example
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe SexpFormatter do
|
16
|
+
should_print 'self'
|
17
|
+
should_print 'Alpha'
|
18
|
+
should_print 'Alpha::Beta'
|
19
|
+
should_print '@@fred'
|
20
|
+
should_print '`ls`'
|
21
|
+
should_print 'array[0]'
|
22
|
+
should_print 'array[0, 1, 2]'
|
23
|
+
should_print 'obj.method(arg1, arg2)'
|
24
|
+
should_print 'obj.method'
|
25
|
+
should_print '$1'
|
26
|
+
should_print 'o=q.downcase'
|
27
|
+
should_print 'true'
|
28
|
+
should_print '"-#{q}xxx#{z.size}"'
|
29
|
+
should_print '0..5'
|
30
|
+
should_print '0..temp'
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/module_context'
|
4
|
+
require 'reek/singleton_method_context'
|
5
|
+
require 'reek/stop_context'
|
6
|
+
|
7
|
+
include Reek
|
8
|
+
|
9
|
+
describe SingletonMethodContext, 'outer_name' do
|
10
|
+
|
11
|
+
it "should report full context" do
|
12
|
+
element = StopContext.new
|
13
|
+
element = ModuleContext.new(element, [0, :mod])
|
14
|
+
element = SingletonMethodContext.new(element, [:defs, [:vcall, :a], :b, nil])
|
15
|
+
element.outer_name.should match(/mod::a\.b/)
|
16
|
+
end
|
17
|
+
end
|
@@ -1,31 +1,23 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
2
|
|
3
|
-
require 'reek/
|
4
|
-
require 'reek/report'
|
3
|
+
require 'reek/smells/control_couple'
|
5
4
|
|
6
|
-
include Reek
|
7
|
-
|
8
|
-
describe MethodChecker, "(Control Couple)" do
|
9
|
-
|
10
|
-
before(:each) do
|
11
|
-
@rpt = Report.new
|
12
|
-
@cchk = MethodChecker.new(@rpt, 'Thing')
|
13
|
-
end
|
5
|
+
include Reek::Smells
|
14
6
|
|
7
|
+
describe ControlCouple do
|
15
8
|
it 'should report a ternary check on a parameter' do
|
16
|
-
|
17
|
-
@rpt.length.should == 1
|
18
|
-
ControlCouple.should === @rpt[0]
|
19
|
-
@rpt[0].to_s.should match(/arga/)
|
9
|
+
'def simple(arga) arga ? @ivar : 3 end'.should reek_of(:ControlCouple, /arga/)
|
20
10
|
end
|
21
11
|
|
22
12
|
it 'should not report a ternary check on an ivar' do
|
23
|
-
|
24
|
-
@rpt.should be_empty
|
13
|
+
'def simple(arga) @ivar ? arga : 3 end'.should_not reek
|
25
14
|
end
|
26
15
|
|
27
16
|
it 'should not report a ternary check on a lvar' do
|
28
|
-
|
29
|
-
|
17
|
+
'def simple(arga) lvar = 27; lvar ? arga : @ivar end'.should_not reek
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should spot a couple inside a block' do
|
21
|
+
'def blocks(arg) @text.map { |blk| arg ? blk : "#{blk}" } end'.should reek_of(:ControlCouple, /arg/)
|
30
22
|
end
|
31
23
|
end
|
@@ -1,60 +1,81 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
-
|
3
|
-
require 'reek/method_checker'
|
4
|
-
require 'reek/smells/duplication'
|
5
2
|
require 'reek/report'
|
3
|
+
require 'reek/method_context'
|
4
|
+
require 'reek/stop_context'
|
5
|
+
require 'reek/smells/duplication'
|
6
6
|
|
7
7
|
include Reek
|
8
8
|
include Reek::Smells
|
9
9
|
|
10
|
-
|
11
|
-
it
|
12
|
-
|
13
|
-
rpt = Report.new
|
14
|
-
cchk = MethodChecker.new(rpt, 'Thing')
|
15
|
-
cchk.check_source(src)
|
16
|
-
rpt.length.should == expected.length
|
17
|
-
(0...rpt.length).each do |smell|
|
18
|
-
expected[smell].each { |patt| rpt[smell].detailed_report.should match(patt) }
|
19
|
-
end
|
10
|
+
describe Duplication, "repeated method calls" do
|
11
|
+
it 'should report repeated call' do
|
12
|
+
'def double_thing() @other.thing + @other.thing end'.should reek_only_of(:Duplication, /@other.thing/)
|
20
13
|
end
|
21
|
-
end
|
22
14
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
15
|
+
it 'should report repeated call to lvar' do
|
16
|
+
'def double_thing() other[@thing] + other[@thing] end'.should reek_only_of(:Duplication, /other\[@thing\]/)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should report call parameters' do
|
20
|
+
'def double_thing() @other.thing(2,3) + @other.thing(2,3) end'.should reek_only_of(:Duplication, /@other.thing\(2, 3\)/)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should report nested calls' do
|
24
|
+
ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.to_source
|
25
|
+
ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
|
26
|
+
ruby.should reek_of(:Duplication, /@other.thing.foo/)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should ignore calls to new' do
|
30
|
+
'def double_thing() @other.new + @other.new end'.should_not reek
|
31
|
+
end
|
34
32
|
end
|
35
33
|
|
36
34
|
describe Duplication, "non-repeated method calls" do
|
37
|
-
|
38
|
-
'def equals(other) other.thing == self.thing end'
|
39
|
-
|
40
|
-
|
35
|
+
it 'should not report similar calls' do
|
36
|
+
'def equals(other) other.thing == self.thing end'.should_not reek
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should respect call parameters' do
|
40
|
+
'def double_thing() @other.thing(3) + @other.thing(2) end'.should_not reek
|
41
|
+
end
|
41
42
|
end
|
42
43
|
|
43
44
|
require 'ostruct'
|
44
45
|
|
45
46
|
describe Duplication, '#examine' do
|
46
|
-
|
47
47
|
before :each do
|
48
48
|
@mc = OpenStruct.new
|
49
|
+
@dup = Duplication.new
|
49
50
|
end
|
50
51
|
|
51
52
|
it 'should return true when reporting a smell' do
|
52
53
|
@mc.calls = {'x' => 47}
|
53
|
-
|
54
|
+
@dup.examine(@mc, []).should == true
|
54
55
|
end
|
55
56
|
|
56
57
|
it 'should return false when not reporting a smell' do
|
57
58
|
@mc.calls = []
|
58
|
-
|
59
|
+
@dup.examine(@mc, []).should == false
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should return false when not reporting calls to new' do
|
63
|
+
@mc.calls = {[:call, :Set, :new] => 4}
|
64
|
+
@dup.examine(@mc, []).should == false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe Duplication, 'when disabled' do
|
69
|
+
before :each do
|
70
|
+
@ctx = MethodContext.new(StopContext.new, [0, :double_thing])
|
71
|
+
@dup = Duplication.new({SmellDetector::ENABLED_KEY => false})
|
72
|
+
@rpt = Report.new
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should not report repeated call' do
|
76
|
+
@ctx.record_call_to([:fred])
|
77
|
+
@ctx.record_call_to([:fred])
|
78
|
+
@dup.examine(@ctx, @rpt).should == false
|
79
|
+
@rpt.length.should == 0
|
59
80
|
end
|
60
81
|
end
|
@@ -1,91 +1,129 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../spec_helper.rb'
|
2
|
-
|
3
|
-
require 'reek/
|
4
|
-
require 'reek/
|
5
|
-
require 'reek/report'
|
2
|
+
require 'reek/smells/feature_envy'
|
3
|
+
require 'reek/method_context'
|
4
|
+
require 'reek/stop_context'
|
6
5
|
|
7
6
|
include Reek
|
8
7
|
include Reek::Smells
|
9
8
|
|
10
|
-
|
11
|
-
it
|
12
|
-
|
13
|
-
rpt = Report.new
|
14
|
-
cchk = MethodChecker.new(rpt, 'Thing')
|
15
|
-
cchk.check_source(src)
|
16
|
-
rpt.length.should == expected.length
|
17
|
-
(0...rpt.length).each do |smell|
|
18
|
-
expected[smell].each { |patt| rpt[smell].detailed_report.should match(patt) }
|
19
|
-
end
|
9
|
+
describe FeatureEnvy, 'with only messages to self' do
|
10
|
+
it 'should not report use of self' do
|
11
|
+
'def simple() self.to_s + self.to_i end'.should_not reek
|
20
12
|
end
|
21
|
-
end
|
22
13
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
14
|
+
it 'should not report vcall with no argument' do
|
15
|
+
'def simple() func; end'.should_not reek
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should not report vcall with argument' do
|
19
|
+
'def simple(arga) func(17); end'.should_not reek
|
20
|
+
end
|
27
21
|
end
|
28
22
|
|
29
23
|
describe FeatureEnvy, 'when the receiver is a parameter' do
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
it 'should not report single use' do
|
25
|
+
'def no_envy(arga) arga.barg(@item) end'.should_not reek
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should not report return value' do
|
29
|
+
'def no_envy(arga) arga.barg(@item); arga end'.should_not reek
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should report many calls to parameter' do
|
33
|
+
'def envy(arga) arga.b(arga) + arga.c(@fred) end'.should reek_only_of(:FeatureEnvy, /arga/)
|
34
|
+
end
|
33
35
|
end
|
34
36
|
|
35
37
|
describe FeatureEnvy, 'when there are many possible receivers' do
|
36
|
-
|
37
|
-
'def total_envy
|
38
|
+
it 'should report highest affinity' do
|
39
|
+
ruby = 'def total_envy
|
38
40
|
fred = @item
|
39
41
|
total = 0
|
40
42
|
total += fred.price
|
41
43
|
total += fred.tax
|
42
44
|
total *= 1.15
|
43
|
-
end'
|
44
|
-
|
45
|
-
|
45
|
+
end'
|
46
|
+
ruby.should reek_only_of(:FeatureEnvy, /total/)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should report multiple affinities' do
|
50
|
+
ruby = 'def total_envy
|
46
51
|
fred = @item
|
47
52
|
total = 0
|
48
53
|
total += fred.price
|
49
54
|
total += fred.tax
|
50
|
-
end'
|
55
|
+
end'
|
56
|
+
ruby.should reek_of(:FeatureEnvy, /total/)
|
57
|
+
ruby.should reek_of(:FeatureEnvy, /fred/)
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
61
|
describe FeatureEnvy, 'when the receiver is external' do
|
54
|
-
|
55
|
-
|
62
|
+
it 'should ignore global variables' do
|
63
|
+
'def no_envy() $s2.to_a; $s2[@item] end'.should_not reek
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should not report class methods' do
|
67
|
+
'def simple() self.class.new.flatten_merge(self) end'.should_not reek
|
68
|
+
end
|
56
69
|
end
|
57
70
|
|
58
71
|
describe FeatureEnvy, 'when the receiver is an ivar' do
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
'def no_envy; @item.price + tax(@item) - savings(@item) end', []
|
63
|
-
end
|
72
|
+
it 'should not report single use of an ivar' do
|
73
|
+
'def no_envy() @item.to_a end'.should_not reek
|
74
|
+
end
|
64
75
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
76
|
+
it 'should not report returning an ivar' do
|
77
|
+
'def no_envy() @item.to_a; @item end'.should_not reek
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should not report ivar usage in a parameter' do
|
81
|
+
'def no_envy; @item.price + tax(@item) - savings(@item) end'.should_not reek
|
82
|
+
end
|
71
83
|
|
72
|
-
|
84
|
+
it 'should not be fooled by duplication' do
|
85
|
+
ruby = Source.from_s('def feed(thing); @cow.feed_to(thing.pig); @duck.feed_to(thing.pig); end')
|
86
|
+
ruby.should reek_only_of(:Duplication, /thing.pig/)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should count local calls' do
|
90
|
+
ruby = Source.from_s('def feed(thing); cow.feed_to(thing.pig); duck.feed_to(thing.pig); end')
|
91
|
+
ruby.should reek_only_of(:Duplication, /thing.pig/)
|
92
|
+
end
|
93
|
+
end
|
73
94
|
|
74
95
|
describe FeatureEnvy, '#examine' do
|
75
96
|
|
76
97
|
before :each do
|
77
|
-
@
|
78
|
-
@
|
79
|
-
@mc.refs = ObjectRefs.new
|
98
|
+
@context = MethodContext.new(StopContext.new, [:defn, :cool])
|
99
|
+
@fe = FeatureEnvy.new
|
80
100
|
end
|
81
101
|
|
82
102
|
it 'should return true when reporting a smell' do
|
83
|
-
@
|
84
|
-
@
|
85
|
-
|
103
|
+
@context.refs.record_ref([:lvar, :thing])
|
104
|
+
@context.refs.record_ref([:lvar, :thing])
|
105
|
+
@fe.examine(@context, []).should == true
|
86
106
|
end
|
87
107
|
|
88
108
|
it 'should return false when not reporting a smell' do
|
89
|
-
|
109
|
+
@fe.examine(@context, []).should == false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe FeatureEnvy, 'when the receiver is an lvar' do
|
114
|
+
it 'should not report single use of an lvar' do
|
115
|
+
'def no_envy() lv = @item; lv.to_a end'.should_not reek
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should not report returning an lvar' do
|
119
|
+
'def no_envy() lv = @item; lv.to_a; lv end'.should_not reek
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should report many calls to lvar' do
|
123
|
+
'def envy; lv = @item; lv.price + lv.tax end'.should reek_only_of(:FeatureEnvy, /lv/)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should not report lvar usage in a parameter' do
|
127
|
+
'def no_envy; lv = @item; lv.price + tax(lv) - savings(lv) end'.should_not reek
|
90
128
|
end
|
91
129
|
end
|