lemon 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|