lemon 0.6 → 0.7.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.
@@ -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