teksymmetry-reek 1.1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/History.txt +131 -0
  2. data/README.txt +36 -0
  3. data/Rakefile +17 -0
  4. data/bin/reek +27 -0
  5. data/config/defaults.reek +51 -0
  6. data/lib/reek/block_context.rb +59 -0
  7. data/lib/reek/class_context.rb +68 -0
  8. data/lib/reek/code_context.rb +54 -0
  9. data/lib/reek/code_parser.rb +221 -0
  10. data/lib/reek/exceptions.reek +13 -0
  11. data/lib/reek/if_context.rb +25 -0
  12. data/lib/reek/method_context.rb +91 -0
  13. data/lib/reek/module_context.rb +33 -0
  14. data/lib/reek/name.rb +49 -0
  15. data/lib/reek/object_refs.rb +52 -0
  16. data/lib/reek/object_source.rb +53 -0
  17. data/lib/reek/options.rb +100 -0
  18. data/lib/reek/rake_task.rb +121 -0
  19. data/lib/reek/report.rb +81 -0
  20. data/lib/reek/sexp_formatter.rb +10 -0
  21. data/lib/reek/singleton_method_context.rb +27 -0
  22. data/lib/reek/smell_warning.rb +49 -0
  23. data/lib/reek/smells/control_couple.rb +61 -0
  24. data/lib/reek/smells/duplication.rb +50 -0
  25. data/lib/reek/smells/feature_envy.rb +58 -0
  26. data/lib/reek/smells/large_class.rb +69 -0
  27. data/lib/reek/smells/long_method.rb +43 -0
  28. data/lib/reek/smells/long_parameter_list.rb +43 -0
  29. data/lib/reek/smells/long_yield_list.rb +18 -0
  30. data/lib/reek/smells/nested_iterators.rb +28 -0
  31. data/lib/reek/smells/smell_detector.rb +66 -0
  32. data/lib/reek/smells/smells.rb +81 -0
  33. data/lib/reek/smells/uncommunicative_name.rb +97 -0
  34. data/lib/reek/smells/utility_function.rb +34 -0
  35. data/lib/reek/source.rb +127 -0
  36. data/lib/reek/spec.rb +146 -0
  37. data/lib/reek/stop_context.rb +50 -0
  38. data/lib/reek/yield_call_context.rb +12 -0
  39. data/lib/reek.rb +7 -0
  40. data/reek.gemspec +44 -0
  41. data/spec/reek/block_context_spec.rb +40 -0
  42. data/spec/reek/class_context_spec.rb +169 -0
  43. data/spec/reek/code_context_spec.rb +93 -0
  44. data/spec/reek/code_parser_spec.rb +34 -0
  45. data/spec/reek/config_spec.rb +42 -0
  46. data/spec/reek/if_context_spec.rb +17 -0
  47. data/spec/reek/method_context_spec.rb +66 -0
  48. data/spec/reek/module_context_spec.rb +38 -0
  49. data/spec/reek/name_spec.rb +13 -0
  50. data/spec/reek/object_refs_spec.rb +131 -0
  51. data/spec/reek/options_spec.rb +13 -0
  52. data/spec/reek/report_spec.rb +48 -0
  53. data/spec/reek/singleton_method_context_spec.rb +17 -0
  54. data/spec/reek/smells/control_couple_spec.rb +23 -0
  55. data/spec/reek/smells/duplication_spec.rb +81 -0
  56. data/spec/reek/smells/feature_envy_spec.rb +221 -0
  57. data/spec/reek/smells/large_class_spec.rb +87 -0
  58. data/spec/reek/smells/long_method_spec.rb +195 -0
  59. data/spec/reek/smells/long_parameter_list_spec.rb +85 -0
  60. data/spec/reek/smells/nested_iterators_spec.rb +33 -0
  61. data/spec/reek/smells/smell_spec.rb +24 -0
  62. data/spec/reek/smells/uncommunicative_name_spec.rb +123 -0
  63. data/spec/reek/smells/utility_function_spec.rb +93 -0
  64. data/spec/slow/inline_spec.rb +40 -0
  65. data/spec/slow/optparse_spec.rb +109 -0
  66. data/spec/slow/redcloth_spec.rb +101 -0
  67. data/spec/slow/reek_source_spec.rb +20 -0
  68. data/spec/slow/samples/inline.rb +704 -0
  69. data/spec/slow/samples/optparse.rb +1788 -0
  70. data/spec/slow/samples/redcloth.rb +1130 -0
  71. data/spec/slow/script_spec.rb +55 -0
  72. data/spec/slow/source_list_spec.rb +40 -0
  73. data/spec/spec.opts +1 -0
  74. data/spec/spec_helper.rb +13 -0
  75. data/tasks/reek.rake +7 -0
  76. data/tasks/rspec.rake +22 -0
  77. metadata +163 -0
@@ -0,0 +1,127 @@
1
+ require 'reek/code_parser'
2
+ require 'reek/report'
3
+ require 'reek/smells/smells'
4
+ require 'ruby_parser'
5
+
6
+ module Reek
7
+
8
+ #
9
+ # A +Source+ object represents a chunk of Ruby source code.
10
+ #
11
+ # The various class methods are factories that will create +Source+
12
+ # instances from various types of input.
13
+ #
14
+ class Source
15
+
16
+ #
17
+ # Factory method: creates a +Source+ object by reading Ruby code from
18
+ # the +IO+ stream. The stream is consumed upto end-of-file, but the
19
+ # source code is not parsed until +report+ is called. +desc+ provides
20
+ # a string description to be used in the header of formatted reports.
21
+ #
22
+ def self.from_io(ios, desc)
23
+ code = ios.readlines.join
24
+ return new(code, desc)
25
+ end
26
+
27
+ #
28
+ # Factory method: creates a +Source+ object by reading Ruby code from
29
+ # the +code+ string. The code is not parsed until +report+ is called.
30
+ #
31
+ def self.from_s(code)
32
+ return new(code, 'string')
33
+ end
34
+
35
+ #
36
+ # Factory method: creates a +Source+ object by reading Ruby code from
37
+ # File +file+. The source code is not parsed until +report+ is called.
38
+ #
39
+ def self.from_f(file)
40
+ from_path(file.path)
41
+ end
42
+
43
+ #
44
+ # Factory method: creates a +Source+ object by reading Ruby code from
45
+ # the named file. The source code is not parsed until +report+ is called.
46
+ #
47
+ def self.from_path(filename)
48
+ code = IO.readlines(filename).join
49
+ return new(code, filename, File.dirname(filename))
50
+ end
51
+
52
+ #
53
+ # Factory method: creates a +Source+ object from an array of file paths.
54
+ # No source code is actually parsed until the report is accessed.
55
+ #
56
+ def self.from_pathlist(paths)
57
+ sources = paths.map {|path| Source.from_path(path) }
58
+ SourceList.new(sources)
59
+ end
60
+
61
+ def initialize(code, desc, dir = nil) # :nodoc:
62
+ @source = code
63
+ @desc = desc
64
+ @cf = SmellConfig.new
65
+ @cf = @cf.load_local(dir) if dir
66
+ end
67
+
68
+ def generate_syntax_tree
69
+ RubyParser.new.parse(@source, @desc) || s()
70
+ end
71
+
72
+ #
73
+ # Returns a +Report+ listing the smells found in this source. The first
74
+ # call to +report+ parses the source code and constructs a list of
75
+ # +SmellWarning+s found; subsequent calls simply return this same list.
76
+ #
77
+ def report
78
+ unless @report
79
+ @report = Report.new
80
+ parser = CodeParser.new(@report, @cf.smell_listeners)
81
+ parser.process(generate_syntax_tree)
82
+ end
83
+ @report
84
+ end
85
+
86
+ def smelly?
87
+ report.length > 0
88
+ end
89
+
90
+ #
91
+ # Checks this source for instances of +smell_class+, and returns +true+
92
+ # only if one of them has a report string matching all of the +patterns+.
93
+ #
94
+ def has_smell?(smell_class, patterns)
95
+ report.any? { |smell| smell.matches?(smell_class, patterns) }
96
+ end
97
+
98
+ # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
99
+ # this report, with a heading.
100
+ def full_report
101
+ report.full_report(@desc)
102
+ end
103
+
104
+ def to_s
105
+ @desc
106
+ end
107
+ end
108
+
109
+ #
110
+ # Represents a list of Sources as if they were a single source.
111
+ #
112
+ class SourceList
113
+ def initialize(sources)
114
+ @sources = sources
115
+ end
116
+
117
+ def smelly?
118
+ @sources.any? {|source| source.smelly? }
119
+ end
120
+
121
+ def report
122
+ ReportList.new(@sources)
123
+ end
124
+ end
125
+ end
126
+
127
+ require 'reek/object_source'
data/lib/reek/spec.rb ADDED
@@ -0,0 +1,146 @@
1
+ require 'reek/source'
2
+
3
+ module Reek
4
+
5
+ #
6
+ # Provides matchers for Rspec, making it easy to check code quality.
7
+ #
8
+ # If you require this module somewhere within your spec (or in your spec_helper),
9
+ # Reek will arrange to update Spec::Runner's config so that it knows about the
10
+ # matchers defined here.
11
+ #
12
+ # === Examples
13
+ #
14
+ # Here's a spec that ensures there are no smell warnings in the current project:
15
+ #
16
+ # describe 'source code quality' do
17
+ # Dir['lib/**/*.rb'].each do |path|
18
+ # it "reports no smells in #{path}" do
19
+ # File.new(path).should_not reek
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ # And here's an even simpler way to do the same:
25
+ #
26
+ # it 'has no code smells' do
27
+ # Dir['lib/**/*.rb'].should_not reek
28
+ # end
29
+ #
30
+ # Here's a simple check of a code fragment:
31
+ #
32
+ # 'def equals(other) other.thing == self.thing end'.should_not reek
33
+ #
34
+ # And a more complex example, making use of one of the factory methods for
35
+ # +Source+ so that the code is parsed and analysed only once:
36
+ #
37
+ # ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.to_source
38
+ # ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
39
+ # ruby.should reek_of(:Duplication, /@other.thing.foo/)
40
+ # ruby.should_not reek_of(:FeatureEnvy)
41
+ #
42
+ module Spec
43
+ class ShouldReek # :nodoc:
44
+ def matches?(actual)
45
+ @source = actual.to_source
46
+ @source.smelly?
47
+ end
48
+ def failure_message_for_should
49
+ "Expected source to reek, but it didn't"
50
+ end
51
+ def failure_message_for_should_not
52
+ "Expected no smells, but got:\n#{@source.report}"
53
+ end
54
+ end
55
+
56
+ #
57
+ # Returns +true+ if and only if the target source code contains smells.
58
+ #
59
+ def reek
60
+ ShouldReek.new
61
+ end
62
+
63
+ class ShouldReekOf # :nodoc:
64
+ def initialize(klass, patterns)
65
+ @klass = klass
66
+ @patterns = patterns
67
+ end
68
+ def matches?(actual)
69
+ @source = actual.to_source
70
+ @source.has_smell?(@klass, @patterns)
71
+ end
72
+ def failure_message_for_should
73
+ "Expected #{@source} to reek of #{@klass}, but it didn't"
74
+ end
75
+ def failure_message_for_should_not
76
+ "Expected #{@source} not to reek of #{@klass}, but got:\n#{@source.report}"
77
+ end
78
+ end
79
+
80
+ #
81
+ # Checks the target source code for instances of +smell_class+,
82
+ # and returns +true+ only if one of them has a report string matching
83
+ # all of the +patterns+.
84
+ #
85
+ def reek_of(smell_class, *patterns)
86
+ ShouldReekOf.new(smell_class, patterns)
87
+ end
88
+
89
+ class ShouldReekOnlyOf # :nodoc:
90
+ def initialize(klass, patterns)
91
+ @klass = klass
92
+ @patterns = patterns
93
+ end
94
+ def matches?(actual)
95
+ @source = actual.to_source
96
+ @source.report.length == 1 and @source.has_smell?(@klass, @patterns)
97
+ end
98
+ def failure_message_for_should
99
+ "Expected source to reek only of #{@klass}, but got:\n#{@source.report}"
100
+ end
101
+ def failure_message_for_should_not
102
+ "Expected source not to reek only of #{@klass}, but it did"
103
+ end
104
+ end
105
+
106
+ #
107
+ # As for reek_of, but the matched smell warning must be the only warning of
108
+ # any kind in the target source code's Reek report.
109
+ #
110
+ def reek_only_of(smell_class, *patterns)
111
+ ShouldReekOnlyOf.new(smell_class, patterns)
112
+ end
113
+ end
114
+ end
115
+
116
+ class File
117
+ def to_source
118
+ Reek::Source.from_f(self)
119
+ end
120
+ end
121
+
122
+ class String
123
+ def to_source
124
+ Reek::Source.from_s(self)
125
+ end
126
+ end
127
+
128
+ class Array
129
+ def to_source
130
+ Reek::Source.from_pathlist(self)
131
+ end
132
+ end
133
+
134
+ module Reek
135
+ class Source
136
+ def to_source
137
+ self
138
+ end
139
+ end
140
+ end
141
+
142
+ if Object.const_defined?(:Spec)
143
+ Spec::Runner.configure do |config|
144
+ config.include(Reek::Spec)
145
+ end
146
+ end
@@ -0,0 +1,50 @@
1
+ module Reek
2
+ class StopContext
3
+
4
+ def initialize
5
+ @refs = ObjectRefs.new
6
+ @myself = Object
7
+ end
8
+ def method_missing(method, *args)
9
+ nil
10
+ end
11
+
12
+
13
+ def count_statements(num)
14
+ 0
15
+ end
16
+
17
+ def find_module(name)
18
+ sym = name.to_s
19
+ @myself.const_defined?(sym) ? @myself.const_get(sym) : nil
20
+ end
21
+
22
+ def has_parameter(sym)
23
+ false
24
+ end
25
+
26
+ def inside_a_block?
27
+ false
28
+ end
29
+
30
+ def is_overriding_method?(name)
31
+ false
32
+ end
33
+
34
+ def num_statements
35
+ 0
36
+ end
37
+
38
+ def refs
39
+ @refs
40
+ end
41
+
42
+ def record_depends_on_self
43
+ false
44
+ end
45
+
46
+ def outer_name
47
+ ''
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ require 'reek/code_context'
2
+
3
+ module Reek
4
+ class YieldCallContext < CodeContext
5
+ attr_reader :parameters
6
+
7
+ def initialize(outer, exp)
8
+ super
9
+ @parameters = exp[1..-1]
10
+ end
11
+ end
12
+ end
data/lib/reek.rb ADDED
@@ -0,0 +1,7 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'reek/verifier_extension_manager'
4
+
5
+ module Reek # :doc:
6
+ VERSION = '1.1.3.1'
7
+ end
data/reek.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{reek}
5
+ s.version = "1.1.3.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Kevin Rutherford"]
9
+ s.date = %q{2009-06-04}
10
+ s.default_executable = %q{reek}
11
+ s.description = %q{Code smell detector for Ruby}
12
+ s.email = ["kevin@rutherford-software.com"]
13
+ s.executables = ["reek"]
14
+ s.extra_rdoc_files = ["History.txt", "README.txt"]
15
+ s.files = ["History.txt", "README.txt", "Rakefile", "bin/reek", "config/defaults.reek", "lib/reek.rb", "lib/reek/block_context.rb", "lib/reek/class_context.rb", "lib/reek/code_context.rb", "lib/reek/code_parser.rb", "lib/reek/exceptions.reek", "lib/reek/if_context.rb", "lib/reek/method_context.rb", "lib/reek/module_context.rb", "lib/reek/name.rb", "lib/reek/object_refs.rb", "lib/reek/object_source.rb", "lib/reek/options.rb", "lib/reek/rake_task.rb", "lib/reek/report.rb", "lib/reek/sexp_formatter.rb", "lib/reek/singleton_method_context.rb", "lib/reek/smell_warning.rb", "lib/reek/smells/control_couple.rb", "lib/reek/smells/duplication.rb", "lib/reek/smells/feature_envy.rb", "lib/reek/smells/large_class.rb", "lib/reek/smells/long_method.rb", "lib/reek/smells/long_parameter_list.rb", "lib/reek/smells/long_yield_list.rb", "lib/reek/smells/nested_iterators.rb", "lib/reek/smells/smell_detector.rb", "lib/reek/smells/smells.rb", "lib/reek/smells/uncommunicative_name.rb", "lib/reek/smells/utility_function.rb", "lib/reek/source.rb", "lib/reek/spec.rb", "lib/reek/stop_context.rb", "lib/reek/yield_call_context.rb", "reek.gemspec", "spec/reek/block_context_spec.rb", "spec/reek/class_context_spec.rb", "spec/reek/code_context_spec.rb", "spec/reek/code_parser_spec.rb", "spec/reek/config_spec.rb", "spec/reek/if_context_spec.rb", "spec/reek/method_context_spec.rb", "spec/reek/module_context_spec.rb", "spec/reek/name_spec.rb", "spec/reek/object_refs_spec.rb", "spec/reek/options_spec.rb", "spec/reek/report_spec.rb", "spec/reek/singleton_method_context_spec.rb", "spec/reek/smells/control_couple_spec.rb", "spec/reek/smells/duplication_spec.rb", "spec/reek/smells/feature_envy_spec.rb", "spec/reek/smells/large_class_spec.rb", "spec/reek/smells/long_method_spec.rb", "spec/reek/smells/long_parameter_list_spec.rb", "spec/reek/smells/nested_iterators_spec.rb", "spec/reek/smells/smell_spec.rb", "spec/reek/smells/uncommunicative_name_spec.rb", "spec/reek/smells/utility_function_spec.rb", "spec/slow/inline_spec.rb", "spec/slow/optparse_spec.rb", "spec/slow/redcloth_spec.rb", "spec/slow/reek_source_spec.rb", "spec/slow/samples/inline.rb", "spec/slow/samples/optparse.rb", "spec/slow/samples/redcloth.rb", "spec/slow/script_spec.rb", "spec/slow/source_list_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/reek.rake", "tasks/rspec.rake"]
16
+ s.homepage = %q{http://wiki.github.com/kevinrutherford/reek}
17
+ s.post_install_message = %q{
18
+ For more information on reek, see http://wiki.github.com/kevinrutherford/reek
19
+ }
20
+ s.rdoc_options = ["--main", "README.txt"]
21
+ s.require_paths = ["lib"]
22
+ s.rubyforge_project = %q{reek}
23
+ s.rubygems_version = %q{1.3.3}
24
+ s.summary = %q{Code smell detector for Ruby}
25
+
26
+ if s.respond_to? :specification_version then
27
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
28
+ s.specification_version = 3
29
+
30
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
31
+ s.add_runtime_dependency(%q<ruby_parser>, ["~> 2.0"])
32
+ s.add_runtime_dependency(%q<ruby2ruby>, ["~> 1.2"])
33
+ s.add_runtime_dependency(%q<sexp_processor>, ["~> 3.0"])
34
+ else
35
+ s.add_dependency(%q<ruby_parser>, ["~> 2.0"])
36
+ s.add_dependency(%q<ruby2ruby>, ["~> 1.2"])
37
+ s.add_dependency(%q<sexp_processor>, ["~> 3.0"])
38
+ end
39
+ else
40
+ s.add_dependency(%q<ruby_parser>, ["~> 2.0"])
41
+ s.add_dependency(%q<ruby2ruby>, ["~> 1.2"])
42
+ s.add_dependency(%q<sexp_processor>, ["~> 3.0"])
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/block_context'
4
+ require 'reek/method_context'
5
+
6
+ include Reek
7
+
8
+ describe BlockContext do
9
+
10
+ it "should record single parameter" do
11
+ element = StopContext.new
12
+ element = BlockContext.new(element, s(s(:lasgn, :x), nil))
13
+ element.variable_names.should == [Name.new(:x)]
14
+ end
15
+
16
+ it "should record single parameter within a method" do
17
+ element = StopContext.new
18
+ element = MethodContext.new(element, s(:defn, :help))
19
+ element = BlockContext.new(element, s(s(:lasgn, :x), nil))
20
+ element.variable_names.should == [Name.new(:x)]
21
+ end
22
+
23
+ it "records multiple parameters" do
24
+ element = StopContext.new
25
+ element = BlockContext.new(element, s(s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))), nil))
26
+ element.variable_names.should == [Name.new(:x), Name.new(:y)]
27
+ end
28
+
29
+ it "should not pass parameters upward" do
30
+ mc = MethodContext.new(StopContext.new, s(:defn, :help))
31
+ element = BlockContext.new(mc, s(s(:lasgn, :x)))
32
+ mc.variable_names.should be_empty
33
+ end
34
+
35
+ it 'records local variables' do
36
+ bctx = BlockContext.new(StopContext.new, nil)
37
+ bctx.record_local_variable(:q2)
38
+ bctx.variable_names.should include(Name.new(:q2))
39
+ end
40
+ end
@@ -0,0 +1,169 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/class_context'
4
+ require 'reek/stop_context'
5
+ require 'reek/smells/feature_envy'
6
+
7
+ include Reek
8
+ include Reek::Smells
9
+
10
+ describe ClassContext do
11
+ it 'should report Long Parameter List' do
12
+ ruby = 'class Inner; def simple(arga, argb, argc, argd) f(3);true end end'
13
+ ruby.should reek_of(:LongParameterList, /Inner/, /simple/, /4 parameters/)
14
+ end
15
+
16
+ it 'should report two different methods' do
17
+ src = <<EOEX
18
+ class Fred
19
+ def simple(arga, argb, argc, argd) f(3);true end
20
+ def simply(arga, argb, argc, argd) f(3);false end
21
+ end
22
+ EOEX
23
+ src.should reek_of(:LongParameterList, /Fred/, /simple/)
24
+ src.should reek_of(:LongParameterList, /Fred/, /simply/)
25
+ end
26
+
27
+ it 'should report many different methods' do
28
+ src = <<EOEX
29
+ class Fred
30
+ def textile_bq(tag, atts, cite, content) f(3);end
31
+ def textile_p(tag, atts, cite, content) f(3);end
32
+ def textile_fn_(tag, num, atts, cite, content) f(3);end
33
+ def textile_popup_help(name, windowW, windowH) f(3);end
34
+ end
35
+ EOEX
36
+ src.should reek_of(:LongParameterList, /Fred/, /textile_bq/)
37
+ src.should reek_of(:LongParameterList, /Fred/, /textile_fn_/)
38
+ src.should reek_of(:LongParameterList, /Fred/, /textile_p/)
39
+ end
40
+ end
41
+
42
+ describe ClassContext, 'overridden methods' do
43
+ class Above
44
+ def above() end
45
+ def both() end
46
+ end
47
+
48
+ class Below < Above
49
+ def both() end
50
+ def below() end
51
+ end
52
+
53
+ describe 'of loaded class' do
54
+ before :each do
55
+ @ctx = ClassContext.create(StopContext.new, [0, :Below])
56
+ end
57
+
58
+ it 'should recognise non-overridden method' do
59
+ @ctx.is_overriding_method?('below').should == false
60
+ @ctx.is_overriding_method?('above').should == false
61
+ end
62
+
63
+ it 'should recognise overridden method' do
64
+ @ctx.is_overriding_method?('both').should == true
65
+ end
66
+
67
+ it 'should recognise methods in current codebase' do
68
+ ctx = ClassContext.create(StopContext.new, [0, :FeatureEnvy])
69
+ ctx.is_overriding_method?('examine_context').should == true
70
+ end
71
+ end
72
+
73
+ describe 'of non-loaded class' do
74
+ before :each do
75
+ @ctx = ClassContext.create(StopContext.new, [0, :Missing])
76
+ end
77
+
78
+ it 'should recognise non-overridden method' do
79
+ @ctx.is_overriding_method?('below').should == false
80
+ @ctx.is_overriding_method?('above').should == false
81
+ end
82
+
83
+ it 'should recognise overridden method' do
84
+ @ctx.is_overriding_method?('both').should == false
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'Integration defect:' do
90
+ it 'should not report UtilityFunction for FeatureEnvy#examine_context' do
91
+ kelement = ClassContext.create(StopContext.new, [0, :FeatureEnvy, s(:const, :SmellDetector)])
92
+ meth = Name.new(:examine_context)
93
+ kelement.is_overriding_method?(meth).should == true
94
+ melement = MethodContext.new(kelement, [0, :examine_context])
95
+ melement.is_overriding_method?(meth).should == true
96
+ melement.depends_on_instance?.should == true
97
+ end
98
+ end
99
+
100
+ describe CodeContext, 'find class' do
101
+ module Mod1
102
+ class Klass1
103
+ module Mod2
104
+ class Klass2
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ before :each do
111
+ @stop = StopContext.new
112
+ @mod1 = ModuleContext.create(@stop, [0, :Mod1])
113
+ @klass1 = ClassContext.create(@mod1, [0, :Klass1])
114
+ @mod2 = ModuleContext.create(@klass1, [0, :Mod2])
115
+ @klass2 = ClassContext.create(@mod2, [0, :Klass2])
116
+ end
117
+
118
+ describe StopContext do
119
+ it 'should not find unqualified class' do
120
+ @stop.find_module('Klass2').should == nil
121
+ end
122
+
123
+ it 'should find unqualified module' do
124
+ @stop.find_module('Mod1').name.should == 'Mod1'
125
+ end
126
+ end
127
+
128
+ describe ModuleContext do
129
+ it 'should find local name' do
130
+ @mod1.find_module('Klass1').name.should == 'Mod1::Klass1'
131
+ @mod2.find_module('Klass2').name.should == 'Mod1::Klass1::Mod2::Klass2'
132
+ end
133
+
134
+ it 'should not find deeper class' do
135
+ @mod1.find_module('Klass2').should == nil
136
+ end
137
+
138
+ it 'should find own Module' do
139
+ @mod1.myself.name.should == 'Mod1'
140
+ @mod2.myself.name.should == 'Mod1::Klass1::Mod2'
141
+ end
142
+ end
143
+
144
+ describe ClassContext do
145
+ it 'should find local module' do
146
+ @klass1.find_module('Mod2').name.should == 'Mod1::Klass1::Mod2'
147
+ end
148
+
149
+ it 'should not find deeper module' do
150
+ @klass1.find_module('Klass2').should == nil
151
+ end
152
+
153
+ it 'should find own Class' do
154
+ @klass1.myself.name.should == 'Mod1::Klass1'
155
+ @klass2.myself.name.should == 'Mod1::Klass1::Mod2::Klass2'
156
+ end
157
+ end
158
+ end
159
+
160
+ describe ClassContext do
161
+ it 'should not report empty class in another module' do
162
+ 'class Treetop::Runtime::SyntaxNode; end'.should_not reek
163
+ end
164
+
165
+ it 'should deal with :: scoped names' do
166
+ element = ClassContext.create(StopContext.new, [:colon2, [:colon2, [:const, :Treetop], :Runtime], :SyntaxNode])
167
+ element.num_methods.should == 0
168
+ end
169
+ end
@@ -0,0 +1,93 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ require 'reek/block_context'
4
+ require 'reek/if_context'
5
+ require 'reek/class_context'
6
+ require 'reek/module_context'
7
+ require 'reek/method_context'
8
+ require 'reek/stop_context'
9
+
10
+ include Reek
11
+
12
+ describe CodeContext, 'to_s' do
13
+
14
+ it "should report full context" do
15
+ element = StopContext.new
16
+ element = ModuleContext.new(element, [0, :mod])
17
+ element = ClassContext.new(element, [0, :klass])
18
+ element = MethodContext.new(element, [0, :bad])
19
+ element = BlockContext.new(element, nil)
20
+ element.to_s.should match(/bad/)
21
+ element.to_s.should match(/klass/)
22
+ element.to_s.should match(/mod/)
23
+ end
24
+
25
+ it "should report method name via if context" do
26
+ element1 = StopContext.new
27
+ element2 = MethodContext.new(element1, [0, :bad])
28
+ element3 = IfContext.new(element2, [0,1])
29
+ BlockContext.new(element3, nil).to_s.should match(/bad/)
30
+ end
31
+
32
+ it "should report method name via nested blocks" do
33
+ element1 = StopContext.new
34
+ element2 = MethodContext.new(element1, [0, :bad])
35
+ element3 = BlockContext.new(element2, nil)
36
+ BlockContext.new(element3, nil).to_s.should match(/bad/)
37
+ end
38
+ end
39
+
40
+ describe CodeContext, 'instance variables' do
41
+ it 'should pass instance variables down to the first class' do
42
+ element = StopContext.new
43
+ element = ModuleContext.new(element, [0, :mod])
44
+ class_element = ClassContext.new(element, [0, :klass])
45
+ element = MethodContext.new(class_element, [0, :bad])
46
+ element = BlockContext.new(element, nil)
47
+ element.record_instance_variable(:fred)
48
+ class_element.variable_names.size.should == 1
49
+ class_element.variable_names.should include(Name.new(:fred))
50
+ end
51
+ end
52
+
53
+ describe CodeContext, 'generics' do
54
+ it 'should pass unknown method calls down the stack' do
55
+ stop = StopContext.new
56
+ def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
57
+ element = ModuleContext.new(stop, [0, :mod])
58
+ class_element = ClassContext.new(element, [0, :klass])
59
+ element = MethodContext.new(class_element, [0, :bad])
60
+ element = BlockContext.new(element, nil)
61
+ element.bananas(17, -5).should == 55
62
+ end
63
+ end
64
+
65
+ describe CodeContext do
66
+ it 'should recognise itself in a collection of names' do
67
+ element = StopContext.new
68
+ element = ModuleContext.new(element, [0, :mod])
69
+ element.matches?(['banana', 'mod']).should == true
70
+ end
71
+
72
+ it 'should recognise itself in a collection of REs' do
73
+ element = StopContext.new
74
+ element = ModuleContext.new(element, [0, :mod])
75
+ element.matches?([/banana/, /mod/]).should == true
76
+ end
77
+
78
+ it 'should recognise its fq name in a collection of names' do
79
+ element = StopContext.new
80
+ element = ModuleContext.new(element, [0, :mod])
81
+ element = ClassContext.create(element, [0, :klass])
82
+ element.matches?(['banana', 'mod']).should == true
83
+ element.matches?(['banana', 'mod::klass']).should == true
84
+ end
85
+
86
+ it 'should recognise its fq name in a collection of names' do
87
+ element = StopContext.new
88
+ element = ModuleContext.new(element, [0, :mod])
89
+ element = ClassContext.create(element, [0, :klass])
90
+ element.matches?([/banana/, /mod/]).should == true
91
+ element.matches?([/banana/, /mod::klass/]).should == true
92
+ end
93
+ end