lemon 0.6 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,13 @@
1
1
  module Lemon
2
- module Commands
2
+ module Command
3
+ require 'lemon/command/abstract'
3
4
 
4
5
  # Lemon Test Command-line tool.
5
- class Test < Command
6
+ class Test < Abstract
6
7
  require 'lemon/runner'
7
8
 
8
- def self.options
9
- []
9
+ def self.subcommand
10
+ 'test'
10
11
  end
11
12
 
12
13
  # Initialize and run.
@@ -17,13 +18,18 @@ module Commands
17
18
  # New Command instance.
18
19
  def initialize
19
20
  @format = nil
21
+ @cover = false
20
22
  @requires = []
21
23
  @includes = []
24
+ @namespaces = []
22
25
  end
23
26
 
24
27
  #
25
28
  attr_accessor :format
26
29
 
30
+ #
31
+ attr_accessor :cover
32
+
27
33
  # Get or set librarires to pre-require.
28
34
  def requires(*paths)
29
35
  @requires.concat(paths) unless paths.empty?
@@ -36,16 +42,33 @@ module Commands
36
42
  @includes
37
43
  end
38
44
 
45
+ #
46
+ def namespaces(*names)
47
+ @namespaces.concat(names) unless names.empty?
48
+ @namespaces
49
+ end
50
+
39
51
  # Instance of OptionParser.
40
52
  def parser
41
53
  @parser ||= OptionParser.new do |opt|
54
+ opt.banner = "lemon [options] [test-files ...]"
42
55
  opt.separator("Run unit tests.")
56
+ opt.separator("OPTIONS:")
57
+ opt.on('--coverage', '-c', "include coverage informarton") do
58
+ self.cover = true
59
+ end
43
60
  opt.on('--verbose', '-v', "select verbose report format") do |type|
44
61
  self.format = :verbose
45
62
  end
46
- #opt.on('--format', '-f [TYPE]', "select alternate report format") do |type|
47
- # self.format = type
48
- #end
63
+ opt.on('--outline', '-o', "select outline report format") do |type|
64
+ self.format = :outline
65
+ end
66
+ opt.on('--format', '-f [TYPE]', "select report format") do |type|
67
+ self.format = type
68
+ end
69
+ opt.on("--namespace", "-n [NAME]", "limit testing to this namespace") do |name|
70
+ namespaces(name)
71
+ end
49
72
  opt.on("-r [FILES]" , 'library files to require') do |files|
50
73
  files = files.split(/[:;]/)
51
74
  requires(*files)
@@ -70,14 +93,12 @@ module Commands
70
93
 
71
94
  files = ARGV.dup
72
95
 
73
- includes.each do |path|
74
- $LOAD_PATHS.unshift(path)
75
- end
76
-
96
+ includes.each{ |path| $LOAD_PATH.unshift(path) }
77
97
  requires.each{ |path| require(path) }
78
98
 
79
- suite = Lemon::Test::Suite.new(*files)
80
- runner = Lemon::Runner.new(suite, format)
99
+ suite = Lemon::Test::Suite.new(files, :cover=>cover)
100
+ runner = Lemon::Runner.new(suite, :format=>format, :cover=>cover, :namespaces=>namespaces)
101
+
81
102
  runner.run
82
103
  end
83
104
 
@@ -1,33 +1,63 @@
1
+ require 'lemon/snapshot'
2
+
1
3
  module Lemon
2
4
 
3
5
  #
4
6
  class Coverage
5
7
 
8
+ #
9
+ attr :suite
10
+
6
11
  # Paths of lemon tests and/or ruby scripts to be compared and covered.
7
12
  # This can include directories too, in which case all .rb scripts below
8
13
  # then directory will be included.
9
14
  attr :files
10
15
 
11
- # Conical snapshot of system (before loading libraries to be covered).
12
- attr :conical
16
+ ## Conical snapshot of system (before loading libraries to be covered).
17
+ #attr :canonical
13
18
 
14
19
  #
15
20
  attr :namespaces
16
21
 
22
+ ## New Coverage object.
23
+ ##
24
+ ## Coverage.new('lib/', :MyApp, :public => true)
25
+ ##
26
+ #def initialize(suite_or_files, namespaces=nil, options={})
27
+ # @namespaces = namespaces || []
28
+ # case suite_or_files
29
+ # when Test::Suite
30
+ # @suite = suite_or_files
31
+ # @files = suite_or_files.files
32
+ # else
33
+ # @suite = Test::Suite.new(suite_or_files)
34
+ # @files = suite_or_files
35
+ # end
36
+ # #@canonical = @suite.canonical
37
+ # @public = options[:public]
38
+ #end
39
+
17
40
  # New Coverage object.
18
41
  #
19
42
  # Coverage.new('lib/', :MyApp, :public => true)
20
43
  #
21
- def initialize(files, namespaces=nil, options={})
22
- @namespaces = namespaces || []
23
-
24
- @files = files
25
- @conical = snapshot
44
+ def initialize(suite, namespaces=nil, options={})
45
+ @namespaces = [namespaces].flatten.compact
46
+ @suite = suite
47
+ @files = suite.files
48
+ #@canonical = @suite.canonical
49
+ @public = options[:public]
50
+ end
26
51
 
27
- @public = options[:public]
52
+ ##
53
+ #def canonical!
54
+ # @canonical = Snapshot.capture
55
+ #end
28
56
 
29
- # this must come after concial snapshot
30
- @suite = Test::Suite.new(files)
57
+ #
58
+ def suite=(suite)
59
+ raise ArgumentError unless Test::Suite === suite
60
+ @suite = suite
31
61
  end
32
62
 
33
63
  # Over use public methods for coverage.
@@ -35,37 +65,37 @@ module Lemon
35
65
  @public
36
66
  end
37
67
 
68
+ #
69
+ def each(&block)
70
+ checklist.each(&block)
71
+ end
72
+
38
73
  # Produce a coverage map.
39
- def coverage(suite)
40
- checklist = cover()
74
+ #def checklist
75
+ # list = system.checklist
76
+ # suite.each do |testcase|
77
+ # testcase.testunits.each do |testunit|
78
+ # list[testcase.target.name][testunit.key] = true
79
+ # end
80
+ # end
81
+ # list
82
+ #end
83
+
84
+ # Produce a coverage checklist.
85
+ def checklist
86
+ list = system.checklist
41
87
  suite.each do |testcase|
42
88
  testcase.testunits.each do |testunit|
43
- checklist[testcase.target.name][testunit.target.to_s] = true
89
+ list[testcase.target.name][testunit.key] = true
44
90
  end
45
91
  end
46
- checklist
92
+ list
47
93
  end
48
94
 
49
- # Coverage template.
50
- def cover
51
- cover = Hash.new{|h,k|h[k]={}}
52
- system.each do |base|
53
- next if base.is_a?(Lemon::Test::Suite)
54
- cover[base.name] = {}
55
- base.public_instance_methods(false).each do |meth|
56
- cover[base.name][meth.to_s] = false
57
- end
58
- unless public_only?
59
- base.private_instance_methods(false).each do |meth|
60
- cover[base.name][meth.to_s] = false
61
- end
62
- base.protected_instance_methods(false).each do |meth|
63
- cover[base.name][meth.to_s] = false
64
- end
65
- end
66
- end
67
- cover
68
- end
95
+ # #
96
+ # def load_covered_files
97
+ # suite.load_covered_files
98
+ # end
69
99
 
70
100
  # Iterate over +paths+ and use #load to bring in all +.rb+ scripts.
71
101
  #def load_system
@@ -80,57 +110,137 @@ module Lemon
80
110
  # files.each{ |file| load(file) }
81
111
  #end
82
112
 
83
- # System to be covered. This takes a sanpshot of the system
84
- # and then removes the conical snapshot, and then filters out
85
- # the namespace.
113
+ # # Snapshot of System to be covered. This takes a current snapshot
114
+ # # of the system and removes the canonical snapshot or filters out
115
+ # # everything but the selected namespace.
116
+ # def system
117
+ # if namespaces.empty?
118
+ # snapshot - canonical
119
+ # else
120
+ # (snapshot - canonical).filter do |ofmod|
121
+ # namespaces.any?{ |n| ofmod.name.start_with?(n.to_s) }
122
+ # end
123
+ # end
124
+ # end
125
+
126
+ # List of modules/classes not covered.
127
+ def uncovered_cases
128
+ c = suite.coverage.map{ |ofmod| ofmod.base }
129
+ a = suite.testcases.map{ |tc| tc.target }
130
+ c - a
131
+ end
132
+
133
+ # List of methods not covered by covered cases.
134
+ def uncovered_units
135
+ @calculated ||= calculate_coverage
136
+ @uncovered_units
137
+ end
138
+
139
+ #
140
+ def undefined_units
141
+ @calculated ||= calculate_coverage
142
+ @undefined_units
143
+ end
144
+
145
+ #
146
+ def calculate_coverage
147
+ clist = []
148
+ tlist = []
149
+ suite.testcases.each do |tc|
150
+ mod = tc.target
151
+
152
+ metaunits, units = *tc.testunits.partition{ |u| u.meta? }
153
+
154
+ units.map!{ |u| u.fullname }
155
+ metaunits.map!{ |u| u.fullname }
156
+
157
+ tlist = tlist | units
158
+ tlist = tlist | metaunits
159
+
160
+ if system[mod]
161
+ meths = system[mod].instance_methods.map{ |m| "#{mod}##{m}" }
162
+ metameths = system[mod].class_methods.map{ |m| "#{mod}.#{m}" }
163
+
164
+ clist = clist | meths
165
+ clist = clist | metameths
166
+ end
167
+ end
168
+ @uncovered_units = clist - tlist
169
+ @undefined_units = tlist - clist
170
+ end
171
+
86
172
  #
87
- # TODO: Perhaps get rid of the conical subtraction and require a namespace?
88
173
  def system
89
174
  if namespaces.empty?
90
- snapshot - conical
175
+ suite.coverage
91
176
  else
92
- snapshot.select do |m|
93
- namespaces.any?{ |n| m.name.start_with?(n) }
177
+ suite.coverage.filter do |ofmod|
178
+ namespaces.any?{ |n| ofmod.name.start_with?(n.to_s) }
94
179
  end
95
180
  end
96
181
  end
97
182
 
98
- # Produces a list of all existent Modules and Classes.
99
- def snapshot
100
- sys = []
101
- #ObjectSpace.each_object(Class) do |c|
102
- # sys << c
103
- #end
104
- ObjectSpace.each_object(Module) do |m|
105
- sys << m
183
+ # Generate code template.
184
+ #
185
+ # TODO: support output
186
+ def generate(output=nil)
187
+ code = []
188
+
189
+ system.each do |ofmod|
190
+ next if ofmod.base.is_a?(Lemon::Test::Suite)
191
+
192
+ code << "TestCase #{ofmod.base} do"
193
+
194
+ ofmod.class_methods(public_only?).each do |meth|
195
+ code << "\n MetaUnit :#{meth} => '' do\n raise Pending\n end"
196
+ end
197
+
198
+ ofmod.instance_methods(public_only?).each do |meth|
199
+ code << "\n Unit :#{meth} => '' do\n raise Pending\n end"
200
+ end
201
+
202
+ code << "\nend\n"
106
203
  end
107
- sys
108
- end
109
204
 
110
- # TODO: combine with coverage to provided option to do only do what hasn't been covered thus far.
111
- # TODO: support output directory
205
+ code.join("\n")
206
+ end
112
207
 
113
- def generate(output=nil)
208
+ #
209
+ def generate_uncovered(output=nil)
114
210
  code = []
115
- system.each do |base|
116
- next if base.is_a?(Lemon::Test::Suite)
211
+ checklist.each do |base, methods|
212
+ next if /Lemon::Test::Suite/ =~ base.to_s
117
213
  code << "TestCase #{base} do"
118
- base.public_instance_methods(false).each do |meth|
119
- code << "\n Unit :#{meth} => '' do\n pending\n end"
120
- end
121
- unless public_only?
122
- base.private_instance_methods(false).each do |meth|
123
- code << "\n Unit :#{meth} => '' do\n pending\n end"
124
- end
125
- base.protected_instance_methods(false).each do |meth|
126
- code << "\n Unit :#{meth} => '' do\n pending\n end"
214
+ methods.each do |meth, covered|
215
+ next if covered
216
+ if meth.to_s =~ /^\:\:/
217
+ meth = meth.sub('::','')
218
+ code << "\n MetaUnit :#{meth} => '' do\n raise Pending\n end"
219
+ else
220
+ code << "\n Unit :#{meth} => '' do\n raise Pending\n end"
127
221
  end
128
222
  end
223
+ #base.public_instance_methods(false).each do |meth|
224
+ # code << "\n Unit :#{meth} => '' do\n Pending\n end"
225
+ #end
226
+ #unless public_only?
227
+ # base.private_instance_methods(false).each do |meth|
228
+ # code << "\n Unit :#{meth} => '' do\n Pending\n end"
229
+ # end
230
+ # base.protected_instance_methods(false).each do |meth|
231
+ # code << "\n Unit :#{meth} => '' do\n Pending\n end"
232
+ # end
233
+ #end
129
234
  code << "\nend\n"
130
235
  end
131
236
  code.join("\n")
132
237
  end
133
238
 
239
+ # Get a snapshot of the system.
240
+ def snapshot
241
+ Snapshot.capture
242
+ end
243
+
134
244
  end#class Coverage
135
245
 
136
246
  end#module Lemon
data/lib/lemon/dsl.rb CHANGED
@@ -28,12 +28,25 @@ end
28
28
  def Case(target_class, &block)
29
29
  Lemon.suite.Case(target_class, &block)
30
30
  end
31
-
32
31
  alias :TestCase :Case
33
32
 
33
+ #
34
+ def Covers(script)
35
+ Lemon.suite.Covers(script)
36
+ end
37
+
38
+ #
39
+ def Helper(script)
40
+ Lemon.suite.Helper(script)
41
+ end
42
+
43
+ #def Subtest(script)
44
+ # Lemon.suite.Subtest(script)
45
+ #end
46
+
34
47
  # FIXME: This is a BIG FAT HACK! For the life of me I cannot find
35
48
  # a way to resolve module constants included in the test cases.
36
- # Becuase of closure, the constant lookup goes through here, and not
49
+ # Because of closure, the constant lookup goes through here, and not
37
50
  # the Case singleton class. So to work around we must note each test
38
51
  # before it is run, and reroute the missing constants.
39
52
  #
data/lib/lemon/kernel.rb CHANGED
@@ -2,19 +2,23 @@ require 'facets/functor'
2
2
 
3
3
  $PRY_TABLE = {} #Hash.new{|h,k| h[k]=nil}
4
4
 
5
- # Pry allows you to test private and protected methods,
6
- # via a public-only interface.
7
- #
8
- # Generally one should avoid testing private and protected
9
- # method directly, instead relying on tests of public methods to
10
- # indirectly test them, because private and protected methods are
11
- # considered implementation details. But sometimes is necessary
12
- # to test them directly, or if you wish to achieve *absolute
13
- # coverage*, say in mission critical systems.
5
+ module Kernel
14
6
 
15
- def pry
16
- $PRY_TABLE[self] ||= Functor.new do |op, *a, &b|
17
- __send__(op, *a, &b)
7
+ # Pry allows you to test private and protected methods,
8
+ # via a public-only interface.
9
+ #
10
+ # Generally one should avoid testing private and protected
11
+ # method directly, instead relying on tests of public methods to
12
+ # indirectly test them, because private and protected methods are
13
+ # considered implementation details. But sometimes is necessary
14
+ # to test them directly, or if you wish to achieve *absolute
15
+ # coverage*, say in mission critical systems.
16
+
17
+ def pry
18
+ $PRY_TABLE[self] ||= Functor.new do |op, *a, &b|
19
+ __send__(op, *a, &b)
20
+ end
18
21
  end
22
+
19
23
  end
20
24