lemon 0.8.1 → 0.8.2
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.
- data/HISTORY.rdoc +15 -0
- data/README.rdoc +32 -14
- data/bin/lemon +3 -2
- data/demo/case_example_fail.rb +15 -0
- data/demo/case_example_pass.rb +32 -0
- data/demo/case_example_pending.rb +14 -0
- data/demo/case_example_untested.rb +10 -0
- data/demo/fixture/example-use.rb +5 -0
- data/demo/fixture/example.rb +20 -0
- data/lib/lemon.rb +2 -2
- data/lib/lemon/cli.rb +281 -0
- data/lib/lemon/controller/coverage_analyzer.rb +343 -0
- data/lib/lemon/controller/scaffold_generator.rb +110 -0
- data/lib/lemon/controller/test_runner.rb +284 -0
- data/lib/lemon/meta/data.rb +29 -0
- data/lib/lemon/meta/gemfile +24 -0
- data/{PROFILE → lib/lemon/meta/profile} +6 -5
- data/lib/lemon/model/ae.rb +4 -0
- data/lib/lemon/model/cover_unit.rb +75 -0
- data/lib/lemon/{dsl.rb → model/main.rb} +22 -28
- data/lib/lemon/model/pending.rb +10 -0
- data/lib/lemon/model/snapshot.rb +203 -0
- data/lib/lemon/model/source_parser.rb +198 -0
- data/lib/lemon/model/test_case.rb +221 -0
- data/lib/lemon/model/test_context.rb +90 -0
- data/lib/lemon/model/test_suite.rb +216 -0
- data/lib/lemon/{test/unit.rb → model/test_unit.rb} +40 -28
- data/lib/lemon/{coversheet → view/cover_reports}/abstract.rb +19 -20
- data/lib/lemon/view/cover_reports/compact.rb +37 -0
- data/lib/lemon/view/cover_reports/outline.rb +45 -0
- data/lib/lemon/view/cover_reports/verbose.rb +51 -0
- data/lib/lemon/view/cover_reports/yaml.rb +15 -0
- data/lib/lemon/view/test_reports/abstract.rb +149 -0
- data/lib/lemon/view/test_reports/dotprogress.rb +73 -0
- data/lib/lemon/view/test_reports/html.rb +146 -0
- data/lib/lemon/view/test_reports/outline.rb +118 -0
- data/lib/lemon/view/test_reports/summary.rb +131 -0
- data/lib/lemon/view/test_reports/tap.rb +49 -0
- data/lib/lemon/view/test_reports/verbose.rb +197 -0
- data/meta/data.rb +29 -0
- data/meta/gemfile +24 -0
- data/meta/profile +17 -0
- data/test/api/applique/fs.rb +18 -0
- data/test/api/coverage/complete.rdoc +136 -0
- data/test/api/coverage/extensions.rdoc +61 -0
- data/test/api/coverage/incomplete.rdoc +97 -0
- data/{features → test/cli}/coverage.feature +4 -4
- data/{features → test/cli}/generate.feature +2 -2
- data/{features → test/cli}/step_definitions/coverage_steps.rb +0 -0
- data/{features → test/cli}/support/ae.rb +0 -0
- data/{features → test/cli}/support/aruba.rb +0 -0
- data/{features → test/cli}/test.feature +0 -0
- data/test/fixtures/case_complete.rb +17 -4
- data/test/fixtures/case_inclusion.rb +18 -0
- data/test/fixtures/case_incomplete.rb +4 -4
- data/test/fixtures/example.rb +5 -0
- data/test/fixtures/helper.rb +13 -0
- data/test/runner +3 -0
- data/test/unit/case_coverage_analyzer.rb +25 -0
- data/test/unit/case_test_case_dsl.rb +46 -0
- metadata +87 -42
- data/REQUIRE +0 -9
- data/VERSION +0 -6
- data/lib/lemon/command.rb +0 -184
- data/lib/lemon/coverage.rb +0 -260
- data/lib/lemon/coversheet/outline.rb +0 -47
- data/lib/lemon/kernel.rb +0 -24
- data/lib/lemon/reporter.rb +0 -22
- data/lib/lemon/reporter/abstract.rb +0 -97
- data/lib/lemon/reporter/dotprogress.rb +0 -68
- data/lib/lemon/reporter/outline.rb +0 -105
- data/lib/lemon/reporter/verbose.rb +0 -143
- data/lib/lemon/runner.rb +0 -308
- data/lib/lemon/snapshot.rb +0 -185
- data/lib/lemon/test/case.rb +0 -139
- data/lib/lemon/test/concern.rb +0 -52
- data/lib/lemon/test/suite.rb +0 -229
- data/test/case_coverage.rb +0 -26
- data/test/case_testcase.rb +0 -58
@@ -0,0 +1,90 @@
|
|
1
|
+
module Lemon
|
2
|
+
|
3
|
+
# Test Instances are used to organize unit tests into groups, so as to address
|
4
|
+
# specific scenarios for a given class.
|
5
|
+
class TestContext
|
6
|
+
|
7
|
+
# The test case to which this concern belongs.
|
8
|
+
attr :testcase
|
9
|
+
|
10
|
+
# The description of this concern. Make this
|
11
|
+
# as detailed as you wish.
|
12
|
+
attr :description
|
13
|
+
|
14
|
+
# New case instance.
|
15
|
+
def initialize(testcase, description, options={}, &block)
|
16
|
+
@testcase = testcase
|
17
|
+
@description = description.to_s
|
18
|
+
@function = options[:function] || options[:singleton]
|
19
|
+
@type = options[:type] || :context
|
20
|
+
@block = block
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
def teardown=(procedure)
|
25
|
+
@teardown = procedure
|
26
|
+
end
|
27
|
+
|
28
|
+
# Teardown instance.
|
29
|
+
def teardown(scope=nil)
|
30
|
+
if scope
|
31
|
+
scope.instance_eval(&@teardown) if @teardown
|
32
|
+
else
|
33
|
+
@teardown
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create instance.
|
38
|
+
def setup(scope)
|
39
|
+
if @block
|
40
|
+
ins = scope.instance_eval(&@block)
|
41
|
+
end
|
42
|
+
ins
|
43
|
+
end
|
44
|
+
|
45
|
+
def function? ; false ; end
|
46
|
+
alias_method :meta?, :function?
|
47
|
+
|
48
|
+
# Returns the description with newlines removed.
|
49
|
+
def to_s
|
50
|
+
description.gsub(/\n/, ' ')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
#class TestInstance < TestContext
|
56
|
+
#
|
57
|
+
# # Create instance.
|
58
|
+
# def setup(scope)
|
59
|
+
# if @block
|
60
|
+
# ins = scope.instance_eval(&@block)
|
61
|
+
# raise "target type mismatch" unless testcase.target === ins
|
62
|
+
# end
|
63
|
+
# ins
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
#end
|
67
|
+
|
68
|
+
=begin
|
69
|
+
#
|
70
|
+
class TestSingleton < TestContext
|
71
|
+
|
72
|
+
# Create instance.
|
73
|
+
def setup(scope)
|
74
|
+
if @block
|
75
|
+
ins = scope.instance_eval(&@block)
|
76
|
+
raise "target type mismatch" unless testcase.target == ins
|
77
|
+
else
|
78
|
+
ins = @testcase.target
|
79
|
+
end
|
80
|
+
ins
|
81
|
+
end
|
82
|
+
|
83
|
+
def function? ; true ; end
|
84
|
+
alias_method :meta?, :function?
|
85
|
+
|
86
|
+
end
|
87
|
+
=end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'lemon/model/test_case'
|
2
|
+
require 'lemon/model/snapshot'
|
3
|
+
#require 'lemon/model/main'
|
4
|
+
|
5
|
+
module Lemon
|
6
|
+
|
7
|
+
# Current suite being defined. This is used
|
8
|
+
# to define a Suite object via the toplevel DSL.
|
9
|
+
def self.suite
|
10
|
+
$lemon_suite #@suite ||= Lemon::TestSuite.new([])
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
def self.suite=(suite)
|
15
|
+
$lemon_suite = suite
|
16
|
+
end
|
17
|
+
|
18
|
+
# Test Suites encapsulate a set of test cases.
|
19
|
+
#
|
20
|
+
class TestSuite
|
21
|
+
|
22
|
+
# Files from which the suite is loaded.
|
23
|
+
attr :files
|
24
|
+
|
25
|
+
# Test cases in this suite.
|
26
|
+
attr :testcases
|
27
|
+
|
28
|
+
# List of pre-test procedures that apply suite-wide.
|
29
|
+
attr :before
|
30
|
+
|
31
|
+
# List of post-test procedures that apply suite-wide.
|
32
|
+
attr :after
|
33
|
+
|
34
|
+
# A snapshot of the system before the suite is loaded.
|
35
|
+
# Only set if +cover+ option is true.
|
36
|
+
#attr :canonical
|
37
|
+
|
38
|
+
# List of files to be covered. This primarily serves
|
39
|
+
# as a means for allowing one test to load another
|
40
|
+
# and ensuring converage remains accurate.
|
41
|
+
#attr :subtest
|
42
|
+
|
43
|
+
#attr :current_file
|
44
|
+
|
45
|
+
#def coverage
|
46
|
+
# @final_coveage ||= @coverage - @canonical
|
47
|
+
#end
|
48
|
+
|
49
|
+
#
|
50
|
+
#attr :options
|
51
|
+
|
52
|
+
attr :dsl
|
53
|
+
|
54
|
+
#
|
55
|
+
def initialize(files, options={})
|
56
|
+
@files = files.flatten
|
57
|
+
@options = options
|
58
|
+
|
59
|
+
@testcases = []
|
60
|
+
|
61
|
+
@before = {}
|
62
|
+
@after = {}
|
63
|
+
|
64
|
+
#load_helpers
|
65
|
+
|
66
|
+
#if cover? or cover_all?
|
67
|
+
# @coverage = Snapshot.new
|
68
|
+
# @canonical = Snapshot.capture
|
69
|
+
#end
|
70
|
+
|
71
|
+
@dsl = DSL.new(self) #, files)
|
72
|
+
|
73
|
+
load_files
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
#class Scope < Module
|
78
|
+
# def initialize
|
79
|
+
# extend self
|
80
|
+
# end
|
81
|
+
#end
|
82
|
+
|
83
|
+
# Iterate through this suite's test cases.
|
84
|
+
def each(&block)
|
85
|
+
@testcases.each(&block)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
def cover?
|
90
|
+
@options[:cover]
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
def cover_all?
|
95
|
+
@options[:cover_all]
|
96
|
+
end
|
97
|
+
|
98
|
+
# TODO: automatic helper loading ?
|
99
|
+
#def load_helpers(*files)
|
100
|
+
# helpers = []
|
101
|
+
# filelist.each do |file|
|
102
|
+
# dir = File.dirname(file)
|
103
|
+
# hlp = Dir[File.join(dir, '{test_,}helper.rb')]
|
104
|
+
# helpers.concat(hlp)
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# helpers.each do |hlp|
|
108
|
+
# require hlp
|
109
|
+
# end
|
110
|
+
#end
|
111
|
+
|
112
|
+
#
|
113
|
+
def load_files #(*files)
|
114
|
+
s = Lemon.suite || self
|
115
|
+
Lemon.suite = self
|
116
|
+
|
117
|
+
filelist.each do |file|
|
118
|
+
#load_file(file)
|
119
|
+
load file #require file
|
120
|
+
end
|
121
|
+
|
122
|
+
Lemon.suite = s
|
123
|
+
|
124
|
+
#if cover?
|
125
|
+
# $stdout << "\n"
|
126
|
+
# $stdout.flush
|
127
|
+
#end
|
128
|
+
|
129
|
+
self #return Lemon.suite
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
#def load_file(file)
|
134
|
+
# #@current_file = file
|
135
|
+
# #if cover_all?
|
136
|
+
# # Covers(file)
|
137
|
+
# #else
|
138
|
+
# file = File.expand_path(file)
|
139
|
+
# @dsl.module_eval(File.read(file), file)
|
140
|
+
# #require(file) #load(file)
|
141
|
+
# #end
|
142
|
+
#end
|
143
|
+
|
144
|
+
# Directories glob *.rb files.
|
145
|
+
def filelist
|
146
|
+
@filelist ||= (
|
147
|
+
files = @files
|
148
|
+
files = files.map{ |f| Dir[f] }.flatten
|
149
|
+
files = files.map do |file|
|
150
|
+
if File.directory?(file)
|
151
|
+
Dir[File.join(file, '**', '*.rb')]
|
152
|
+
else
|
153
|
+
file
|
154
|
+
end
|
155
|
+
end.flatten
|
156
|
+
#files = files.map{ |f| File.expand_path(f) }
|
157
|
+
files.uniq
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
class DSL < Module
|
162
|
+
#
|
163
|
+
def initialize(test_suite)
|
164
|
+
@test_suite = test_suite
|
165
|
+
#module_eval(&code)
|
166
|
+
end
|
167
|
+
|
168
|
+
# TODO: need require_find() to avoid first snapshot ?
|
169
|
+
def covers(file)
|
170
|
+
#if @test_suite.cover?
|
171
|
+
# #return if $".include?(file)
|
172
|
+
# s = Snapshot.capture
|
173
|
+
# if require(file)
|
174
|
+
# z = Snapshot.capture
|
175
|
+
# @test_suite.coverage << (z - s)
|
176
|
+
# end
|
177
|
+
#else
|
178
|
+
require file
|
179
|
+
#end
|
180
|
+
end
|
181
|
+
alias_method :Covers, :covers
|
182
|
+
|
183
|
+
# Define a test case belonging to this suite.
|
184
|
+
def testcase(target_class, &block)
|
185
|
+
raise "lemon: case target must be a class or module" unless Module === target_class
|
186
|
+
@test_suite.testcases << TestCase.new(@test_suite, target_class, &block)
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
alias_method :TestCase, :testcase
|
191
|
+
alias_method :tests, :testcase
|
192
|
+
|
193
|
+
# Define a pre-test procedure to apply suite-wide.
|
194
|
+
def before(*matches, &block)
|
195
|
+
@test_suite.before[matches] = block #<< Advice.new(match, &block)
|
196
|
+
end
|
197
|
+
alias_method :Before, :before
|
198
|
+
|
199
|
+
# Define a post-test procedure to apply suite-wide.
|
200
|
+
def after(*matches, &block)
|
201
|
+
@test_suite.after[matches] = block #<< Advice.new(match, &block)
|
202
|
+
end
|
203
|
+
alias_method :After, :after
|
204
|
+
|
205
|
+
# Includes at the suite level are routed to the toplevel.
|
206
|
+
#def include(*mods)
|
207
|
+
# TOPLEVEL_BINDING.eval('self').instance_eval do
|
208
|
+
# include(*mods)
|
209
|
+
# end
|
210
|
+
#end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
@@ -1,54 +1,58 @@
|
|
1
|
-
module Lemon
|
1
|
+
module Lemon
|
2
2
|
|
3
3
|
#
|
4
|
-
class
|
4
|
+
class TestUnit
|
5
5
|
|
6
6
|
# The test case to which this unit test belongs.
|
7
7
|
attr :testcase
|
8
8
|
|
9
|
-
# The
|
10
|
-
attr :
|
9
|
+
# The context to use for this test.
|
10
|
+
attr :context
|
11
11
|
|
12
12
|
# A test unit +target+ is a method.
|
13
13
|
attr :target
|
14
14
|
|
15
|
-
# The aspect of the
|
15
|
+
# The aspect of the instance this test fulfills.
|
16
16
|
attr :aspect
|
17
17
|
|
18
18
|
# Test procedure, in which test assertions should be made.
|
19
19
|
attr :procedure
|
20
20
|
|
21
21
|
# New unit test.
|
22
|
-
def initialize(
|
23
|
-
|
24
|
-
|
25
|
-
@concern = concern
|
26
|
-
@testcase = concern.testcase
|
27
|
-
|
22
|
+
def initialize(testcase, target, options={}, &procedure)
|
23
|
+
@testcase = testcase
|
28
24
|
@target = target
|
29
25
|
|
30
26
|
@aspect = options[:aspect]
|
31
|
-
@
|
27
|
+
@function = options[:function] || options[:metaclass]
|
28
|
+
@context = options[:context]
|
29
|
+
@omit = options[:omit]
|
32
30
|
|
33
31
|
@procedure = procedure
|
32
|
+
|
33
|
+
@tested = false
|
34
34
|
end
|
35
35
|
|
36
|
-
#
|
37
|
-
|
38
|
-
|
36
|
+
#
|
37
|
+
attr_accessor :omit
|
38
|
+
|
39
|
+
#
|
40
|
+
def name ; @target ; end
|
41
|
+
|
42
|
+
# Is this unit test for a class or module level method?
|
43
|
+
def function?
|
44
|
+
@function
|
39
45
|
end
|
46
|
+
alias_method :meta?, :function?
|
40
47
|
|
41
|
-
#
|
42
|
-
def
|
43
|
-
|
44
|
-
begin
|
45
|
-
Lemon.test_stack << self # hack
|
46
|
-
procedure.call
|
47
|
-
ensure
|
48
|
-
Lemon.test_stack.pop
|
49
|
-
end
|
48
|
+
#
|
49
|
+
def omit?
|
50
|
+
@omit
|
50
51
|
end
|
51
52
|
|
53
|
+
#
|
54
|
+
attr_accessor :tested
|
55
|
+
|
52
56
|
# The suite to which this unit test belongs.
|
53
57
|
def suite
|
54
58
|
testcase.suite
|
@@ -74,9 +78,19 @@ module Lemon::Test
|
|
74
78
|
#
|
75
79
|
def to_s
|
76
80
|
if meta?
|
77
|
-
"#{testcase}.#{target}
|
81
|
+
"#{testcase}.#{target}"
|
78
82
|
else
|
79
|
-
"#{testcase}##{target}
|
83
|
+
"#{testcase}##{target}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
def description
|
89
|
+
if meta?
|
90
|
+
"#{testcase} #{instance} .#{target} #{aspect}"
|
91
|
+
else
|
92
|
+
a = /^[aeiou]/i =~ testcase.to_s ? 'An' : 'A'
|
93
|
+
"#{a} #{testcase} #{instance} receiving ##{target} #{aspect}"
|
80
94
|
end
|
81
95
|
end
|
82
96
|
|
@@ -88,5 +102,3 @@ module Lemon::Test
|
|
88
102
|
|
89
103
|
end
|
90
104
|
|
91
|
-
|
92
|
-
|
@@ -1,5 +1,4 @@
|
|
1
|
-
module Lemon
|
2
|
-
module CoverSheet
|
1
|
+
module Lemon::CoverReports
|
3
2
|
|
4
3
|
class Abstract
|
5
4
|
|
@@ -21,40 +20,40 @@ module CoverSheet
|
|
21
20
|
#
|
22
21
|
attr :coverage
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
#
|
24
|
+
def render
|
25
|
+
end
|
26
|
+
|
27
|
+
def covered_units
|
28
|
+
coverage.covered
|
26
29
|
end
|
27
30
|
|
28
31
|
def uncovered_units
|
29
|
-
coverage.
|
32
|
+
coverage.uncovered
|
30
33
|
end
|
31
34
|
|
32
35
|
def undefined_units
|
33
|
-
coverage.
|
36
|
+
coverage.undefined
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
@ansicolor ? ANSI::Code.red{ string } : string
|
39
|
+
def uncovered_cases
|
40
|
+
coverage.uncovered_cases
|
39
41
|
end
|
40
42
|
|
41
43
|
#
|
42
|
-
def
|
43
|
-
|
44
|
-
|
44
|
+
def tally
|
45
|
+
c = covered_units.size
|
46
|
+
u = uncovered_units.size
|
47
|
+
t = c + u
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
@ansicolor ? ANSI::Code.green{ string } : string
|
49
|
-
end
|
49
|
+
pc = c * 100 / t
|
50
|
+
pu = u * 100 / t
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
"#{uncovered_cases.size} uncovered cases, #{uncovered_units.size} uncovered units, #{undefined_units.size} undefined units"
|
52
|
+
"#{pc}% #{c}/#{t} covered, #{pu}% #{u}/#{t} uncovered" +
|
53
|
+
" (#{undefined_units.size} undefined units, #{uncovered_cases.size} uncovered cases)"
|
54
54
|
end
|
55
55
|
|
56
56
|
end
|
57
57
|
|
58
58
|
end
|
59
|
-
end
|
60
59
|
|