lemon 0.6 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +32 -1
- data/LICENSE +22 -0
- data/README.rdoc +30 -34
- data/lib/lemon/command.rb +16 -39
- data/lib/lemon/command/abstract.rb +29 -0
- data/lib/lemon/{commands → command}/coverage.rb +13 -14
- data/lib/lemon/{commands → command}/generate.rb +35 -19
- data/lib/lemon/{commands → command}/test.rb +34 -13
- data/lib/lemon/coverage.rb +175 -65
- data/lib/lemon/dsl.rb +15 -2
- data/lib/lemon/kernel.rb +16 -12
- data/lib/lemon/reporter.rb +17 -47
- data/lib/lemon/reporter/abstract.rb +92 -0
- data/lib/lemon/{reporters → reporter}/dotprogress.rb +5 -7
- data/lib/lemon/reporter/outline.rb +105 -0
- data/lib/lemon/reporter/verbose.rb +127 -0
- data/lib/lemon/runner.rb +195 -12
- data/lib/lemon/snapshot.rb +185 -0
- data/lib/lemon/test/case.rb +33 -11
- data/lib/lemon/test/concern.rb +10 -1
- data/lib/lemon/test/suite.rb +128 -22
- data/lib/lemon/test/unit.rb +43 -7
- data/meta/{author → authors} +0 -0
- data/meta/version +1 -1
- data/test/{cases/coverage_case.rb → coverage_case.rb} +4 -6
- data/test/{cases/testcase_case.rb → testcase_case.rb} +1 -1
- metadata +33 -31
- data/COPYING +0 -621
- data/COPYING.LESSER +0 -166
- data/MANIFEST +0 -43
- data/Syckfile +0 -68
- data/lib/lemon/reporters.rb +0 -3
- data/lib/lemon/reporters/verbose.rb +0 -92
@@ -1,12 +1,13 @@
|
|
1
1
|
module Lemon
|
2
|
-
module
|
2
|
+
module Command
|
3
|
+
require 'lemon/command/abstract'
|
3
4
|
|
4
5
|
# Lemon Test Command-line tool.
|
5
|
-
class Test <
|
6
|
+
class Test < Abstract
|
6
7
|
require 'lemon/runner'
|
7
8
|
|
8
|
-
def self.
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
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(
|
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
|
|
data/lib/lemon/coverage.rb
CHANGED
@@ -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
|
-
|
12
|
-
attr :
|
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(
|
22
|
-
@namespaces = namespaces
|
23
|
-
|
24
|
-
@files
|
25
|
-
|
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
|
-
|
52
|
+
##
|
53
|
+
#def canonical!
|
54
|
+
# @canonical = Snapshot.capture
|
55
|
+
#end
|
28
56
|
|
29
|
-
|
30
|
-
|
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
|
40
|
-
|
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
|
-
|
89
|
+
list[testcase.target.name][testunit.key] = true
|
44
90
|
end
|
45
91
|
end
|
46
|
-
|
92
|
+
list
|
47
93
|
end
|
48
94
|
|
49
|
-
#
|
50
|
-
def
|
51
|
-
|
52
|
-
|
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
|
84
|
-
# and
|
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
|
-
|
175
|
+
suite.coverage
|
91
176
|
else
|
92
|
-
|
93
|
-
namespaces.any?{ |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
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
111
|
-
|
205
|
+
code.join("\n")
|
206
|
+
end
|
112
207
|
|
113
|
-
|
208
|
+
#
|
209
|
+
def generate_uncovered(output=nil)
|
114
210
|
code = []
|
115
|
-
|
116
|
-
next if
|
211
|
+
checklist.each do |base, methods|
|
212
|
+
next if /Lemon::Test::Suite/ =~ base.to_s
|
117
213
|
code << "TestCase #{base} do"
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
|