reek 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|