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.
- 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
@@ -0,0 +1,185 @@
|
|
1
|
+
module Lemon
|
2
|
+
|
3
|
+
#
|
4
|
+
class Snapshot
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
#
|
8
|
+
def self.capture
|
9
|
+
o = new
|
10
|
+
o.capture
|
11
|
+
o
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
attr :modules
|
16
|
+
|
17
|
+
#
|
18
|
+
def initialize
|
19
|
+
@modules = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
def each(&block)
|
24
|
+
modules.values.each(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
def size
|
29
|
+
modules.size
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
def [](mod)
|
34
|
+
@modules[mod]
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
def []=(mod, ofmod)
|
39
|
+
@modules[mod] = ofmod
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def capture
|
44
|
+
@modules = {}
|
45
|
+
ObjectSpace.each_object(Module) do |mod|
|
46
|
+
next if mod.name.empty?
|
47
|
+
@modules[mod] = OfModule.new(mod)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
def to_a(public_only=true)
|
53
|
+
modules.values.map{ |m| m.to_a(public_only) }.flatten
|
54
|
+
end
|
55
|
+
|
56
|
+
# Produce a hash based checklist thap mod #.namet Coverage uses
|
57
|
+
# to compare against tests and create a coverage report.
|
58
|
+
def checklist(public_only=true)
|
59
|
+
h = Hash.new{|h,k|h[k]={}}
|
60
|
+
modules.values.each do |mod|
|
61
|
+
mod.class_methods(public_only).each do |meth|
|
62
|
+
h[mod.name]["::#{meth}"] = false
|
63
|
+
end
|
64
|
+
mod.instance_methods(public_only).each do |meth|
|
65
|
+
h[mod.name]["#{meth}"] = false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
h
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
def -(other)
|
73
|
+
c = Snapshot.new
|
74
|
+
modules.each do |mod, ofmod|
|
75
|
+
c[mod] = ofmod.dup
|
76
|
+
end
|
77
|
+
other.modules.each do |mod, ofmod|
|
78
|
+
if c[mod]
|
79
|
+
c[mod].public_instance_methods -= ofmod.public_instance_methods
|
80
|
+
c[mod].private_instance_methods -= ofmod.private_instance_methods
|
81
|
+
c[mod].protected_instance_methods -= ofmod.protected_instance_methods
|
82
|
+
|
83
|
+
c[mod].public_class_methods -= ofmod.public_class_methods
|
84
|
+
c[mod].private_class_methods -= ofmod.private_class_methods
|
85
|
+
c[mod].protected_class_methods -= ofmod.protected_class_methods
|
86
|
+
end
|
87
|
+
end
|
88
|
+
c.clean!
|
89
|
+
return c
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
def <<(other)
|
94
|
+
other.modules.each do |mod, ofmod|
|
95
|
+
if self[mod]
|
96
|
+
self[mod].public_instance_methods += ofmod.public_instance_methods
|
97
|
+
self[mod].private_instance_methods += ofmod.private_instance_methods
|
98
|
+
self[mod].protected_instance_methods += ofmod.protected_instance_methods
|
99
|
+
|
100
|
+
self[mod].public_class_methods += ofmod.public_class_methods
|
101
|
+
self[mod].private_class_methods += ofmod.private_class_methods
|
102
|
+
self[mod].protected_class_methods += ofmod.protected_class_methods
|
103
|
+
else
|
104
|
+
self[mod] = ofmod.dup
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
def clean!
|
111
|
+
modules.each do |mod, ofmod|
|
112
|
+
if ofmod.class_methods(false).empty? && ofmod.instance_methods(false).empty?
|
113
|
+
modules.delete(mod)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
def filter(&block)
|
120
|
+
c = Snapshot.new
|
121
|
+
modules.each do |mod, ofmod|
|
122
|
+
if block.call(ofmod)
|
123
|
+
c[mod] = ofmod
|
124
|
+
end
|
125
|
+
end
|
126
|
+
c
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
class OfModule
|
131
|
+
attr :base
|
132
|
+
|
133
|
+
attr_accessor :public_instance_methods
|
134
|
+
attr_accessor :protected_instance_methods
|
135
|
+
attr_accessor :private_instance_methods
|
136
|
+
|
137
|
+
attr_accessor :public_class_methods
|
138
|
+
attr_accessor :protected_class_methods
|
139
|
+
attr_accessor :private_class_methods
|
140
|
+
|
141
|
+
#
|
142
|
+
def initialize(base)
|
143
|
+
@base = base
|
144
|
+
|
145
|
+
@public_instance_methods = base.public_instance_methods(false)
|
146
|
+
@protected_instance_methods = base.protected_instance_methods(false)
|
147
|
+
@private_instance_methods = base.private_instance_methods(false)
|
148
|
+
|
149
|
+
@public_class_methods = base.methods(false)
|
150
|
+
@protected_class_methods = base.protected_methods(false)
|
151
|
+
@private_class_methods = base.private_methods(false)
|
152
|
+
end
|
153
|
+
|
154
|
+
#
|
155
|
+
def name
|
156
|
+
@base.name
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
def instance_methods(public_only=true)
|
161
|
+
if public_only
|
162
|
+
public_instance_methods
|
163
|
+
else
|
164
|
+
public_instance_methods + private_instance_methods + protected_instance_methods
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
#
|
169
|
+
def class_methods(public_only=true)
|
170
|
+
if public_only
|
171
|
+
public_class_methods
|
172
|
+
else
|
173
|
+
public_class_methods + private_class_methods + protected_class_methods
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
def to_a(public_only=true)
|
179
|
+
class_methods(public_only).map{ |m| "#{name}.#{m}" } + instance_methods(public_only).map{ |m| "#{name}##{m}" }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
data/lib/lemon/test/case.rb
CHANGED
@@ -50,8 +50,9 @@ module Lemon::Test
|
|
50
50
|
end
|
51
51
|
|
52
52
|
# Define a new test concern for this case.
|
53
|
-
|
54
|
-
|
53
|
+
# TODO: Probably will deprecate the &setup procedure (YAGNI).
|
54
|
+
def Concern(*description, &setup)
|
55
|
+
concern = Concern.new(self, description, &setup)
|
55
56
|
@concerns << concern
|
56
57
|
end
|
57
58
|
|
@@ -74,7 +75,7 @@ module Lemon::Test
|
|
74
75
|
def Unit(*targets, &block)
|
75
76
|
targets_hash = Hash===targets.last ? targets.pop : {}
|
76
77
|
targets_hash.each do |target_method, target_concern|
|
77
|
-
@testunits << Unit.new(current_concern, target_method, target_concern, &block)
|
78
|
+
@testunits << Unit.new(current_concern, target_method, :aspect=>target_concern, &block)
|
78
79
|
end
|
79
80
|
targets.each do |target_method|
|
80
81
|
@testunits << Unit.new(current_concern, target_method, &block)
|
@@ -82,16 +83,36 @@ module Lemon::Test
|
|
82
83
|
end
|
83
84
|
alias_method :unit, :Unit
|
84
85
|
|
86
|
+
# Define a meta-method unit test for this case.
|
87
|
+
def MetaUnit(*targets, &block)
|
88
|
+
targets_hash = Hash===targets.last ? targets.pop : {}
|
89
|
+
targets_hash.each do |target_method, target_concern|
|
90
|
+
@testunits << Unit.new(current_concern, target_method, :aspect=>target_concern, :metaclass=>true, &block)
|
91
|
+
end
|
92
|
+
targets.each do |target_method|
|
93
|
+
@testunits << Unit.new(current_concern, target_method, :metaclass=>true, &block)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
alias_method :metaunit, :MetaUnit
|
97
|
+
|
85
98
|
# Define a before procedure for this case.
|
86
|
-
def Before(
|
87
|
-
|
99
|
+
def Before(*matches, &block)
|
100
|
+
matches == [nil] if matches.empty?
|
101
|
+
matches.each do |match|
|
102
|
+
@before_clauses[match] = block #<< Advice.new(match, &block)
|
103
|
+
end
|
88
104
|
end
|
105
|
+
|
89
106
|
alias_method :before, :Before
|
90
107
|
|
91
108
|
# Define an after procedure for this case.
|
92
|
-
def After(
|
93
|
-
|
109
|
+
def After(*matches, &block)
|
110
|
+
matches == [nil] if matches.empty?
|
111
|
+
matches.each do |match|
|
112
|
+
@after_clauses[match] = block #<< Advice.new(match, &block)
|
113
|
+
end
|
94
114
|
end
|
115
|
+
|
95
116
|
alias_method :after, :After
|
96
117
|
|
97
118
|
# Define a concern procedure to apply case-wide.
|
@@ -100,9 +121,9 @@ module Lemon::Test
|
|
100
121
|
end
|
101
122
|
|
102
123
|
#
|
103
|
-
def pending
|
104
|
-
|
105
|
-
end
|
124
|
+
#def pending
|
125
|
+
# raise Pending
|
126
|
+
#end
|
106
127
|
|
107
128
|
#
|
108
129
|
def to_s
|
@@ -112,6 +133,7 @@ module Lemon::Test
|
|
112
133
|
|
113
134
|
end
|
114
135
|
|
115
|
-
class
|
136
|
+
class Pending < Assertion
|
137
|
+
def self.to_proc; lambda{ raise self }; end
|
116
138
|
end
|
117
139
|
|
data/lib/lemon/test/concern.rb
CHANGED
@@ -15,10 +15,14 @@ module Lemon::Test
|
|
15
15
|
# Unit tests that belong to this concern.
|
16
16
|
attr :testunits
|
17
17
|
|
18
|
+
# Setup procedure for concern.
|
19
|
+
attr :setup
|
20
|
+
|
18
21
|
# New concern.
|
19
|
-
def initialize(testcase, *description)
|
22
|
+
def initialize(testcase, *description, &setup)
|
20
23
|
@testcase = testcase
|
21
24
|
@description = description.join("\n")
|
25
|
+
@setup = setup
|
22
26
|
@testunits = []
|
23
27
|
end
|
24
28
|
|
@@ -38,6 +42,11 @@ module Lemon::Test
|
|
38
42
|
description.gsub(/\n/, ' ')
|
39
43
|
end
|
40
44
|
|
45
|
+
#
|
46
|
+
def call
|
47
|
+
setup.call if setup
|
48
|
+
end
|
49
|
+
|
41
50
|
end
|
42
51
|
|
43
52
|
end
|
data/lib/lemon/test/suite.rb
CHANGED
@@ -8,6 +8,9 @@ module Test
|
|
8
8
|
#
|
9
9
|
class Suite
|
10
10
|
|
11
|
+
# Files from which the suite is loaded.
|
12
|
+
attr :files
|
13
|
+
|
11
14
|
# Test cases in this suite.
|
12
15
|
attr :testcases
|
13
16
|
|
@@ -20,46 +23,120 @@ module Test
|
|
20
23
|
# List of post-test procedures that apply suite-wide.
|
21
24
|
attr :after_clauses
|
22
25
|
|
26
|
+
## A snapshot of the system before the suite is loaded.
|
27
|
+
#attr :canonical
|
28
|
+
|
29
|
+
# List of files to be covered. This primarily serves
|
30
|
+
# as a means for allowing one test to load another
|
31
|
+
# and ensuring converage remains accurate.
|
32
|
+
#attr :subtest
|
33
|
+
|
34
|
+
def coverage
|
35
|
+
@final_coveage ||= @coverage - @canonical
|
36
|
+
end
|
37
|
+
|
23
38
|
#
|
24
|
-
|
39
|
+
#attr :options
|
40
|
+
|
41
|
+
#
|
42
|
+
def initialize(files, options={})
|
43
|
+
@files = files.flatten
|
44
|
+
@options = options
|
45
|
+
|
46
|
+
#@subtest = []
|
25
47
|
@testcases = []
|
26
48
|
@before_clauses = {}
|
27
49
|
@after_clauses = {}
|
28
50
|
@when_clauses = {}
|
29
51
|
|
30
|
-
|
52
|
+
#load_helpers
|
53
|
+
|
54
|
+
@coverage = Snapshot.new
|
55
|
+
@canonical = Snapshot.capture
|
56
|
+
|
57
|
+
load_files
|
58
|
+
#load_subtest_helpers
|
31
59
|
end
|
32
60
|
|
33
61
|
#
|
34
|
-
def
|
35
|
-
|
62
|
+
def cover?
|
63
|
+
@options[:cover]
|
64
|
+
end
|
36
65
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
66
|
+
#
|
67
|
+
#def load_helpers(*files)
|
68
|
+
# helpers = []
|
69
|
+
# filelist.each do |file|
|
70
|
+
# dir = File.dirname(file)
|
71
|
+
# hlp = Dir[File.join(dir, '{test_,}helper.rb')]
|
72
|
+
# helpers.concat(hlp)
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# helpers.each do |hlp|
|
76
|
+
# require hlp
|
77
|
+
# end
|
78
|
+
#end
|
45
79
|
|
46
|
-
|
80
|
+
#def load_subtest_helpers
|
81
|
+
# helpers = []
|
82
|
+
# @subtest.each do |file|
|
83
|
+
# dir = File.dirname(file)
|
84
|
+
# hlp = Dir[File.join(dir, '{test_,}helper.rb')]
|
85
|
+
# helpers.concat(hlp)
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# #s = Snapshot.capture
|
89
|
+
# helpers.each do |hlp|
|
90
|
+
# require hlp
|
91
|
+
# end
|
92
|
+
# #z = Snapshot.capture
|
93
|
+
# #d = z - s
|
94
|
+
# #@canonical << d
|
95
|
+
#end
|
96
|
+
|
97
|
+
#
|
98
|
+
def load_files #(*files)
|
99
|
+
#if cover?
|
100
|
+
# $stdout << "Load: "
|
101
|
+
#end
|
102
|
+
|
103
|
+
Lemon.suite = self
|
104
|
+
filelist.each do |file|
|
105
|
+
#@current_file = file
|
47
106
|
#file = File.expand_path(file)
|
48
107
|
#instance_eval(File.read(file), file)
|
49
|
-
load(file)
|
108
|
+
require(file) #load(file)
|
50
109
|
end
|
51
110
|
|
111
|
+
#if cover?
|
112
|
+
# $stdout << "\n"
|
113
|
+
# $stdout.flush
|
114
|
+
#end
|
115
|
+
|
52
116
|
return Lemon.suite
|
53
117
|
end
|
54
118
|
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
119
|
+
# Directories glob *.rb files.
|
120
|
+
def filelist
|
121
|
+
@filelist ||= (
|
122
|
+
@files.flatten.map do |file|
|
123
|
+
if File.directory?(file)
|
124
|
+
Dir[File.join(file, '**', '*.rb')]
|
125
|
+
else
|
126
|
+
file
|
127
|
+
end
|
128
|
+
end.flatten.uniq
|
129
|
+
)
|
61
130
|
end
|
62
131
|
|
132
|
+
## Load a helper. This method must be used when loading local
|
133
|
+
## suite support. The usual #require or #load can only be used
|
134
|
+
## for extenal support libraries (such as a test mock framework).
|
135
|
+
## This is so because suite code is not evaluated at the toplevel.
|
136
|
+
#def helper(file)
|
137
|
+
# instance_eval(File.read(file), file)
|
138
|
+
#end
|
139
|
+
|
63
140
|
#
|
64
141
|
#def load(file)
|
65
142
|
# instance_eval(File.read(file), file)
|
@@ -74,13 +151,12 @@ module Test
|
|
74
151
|
|
75
152
|
# Define a test case belonging to this suite.
|
76
153
|
def Case(target_class, &block)
|
154
|
+
raise "lemon: case target must be a class or module" unless Module === target_class
|
77
155
|
testcases << Case.new(self, target_class, &block)
|
78
156
|
end
|
79
157
|
|
80
158
|
#
|
81
159
|
alias_method :TestCase, :Case
|
82
|
-
|
83
|
-
#
|
84
160
|
#alias_method :testcase, :Case
|
85
161
|
|
86
162
|
# Define a pre-test procedure to apply suite-wide.
|
@@ -102,6 +178,36 @@ module Test
|
|
102
178
|
@when_clauses[match] = block #<< Advice.new(match, &block)
|
103
179
|
end
|
104
180
|
|
181
|
+
# TODO: need require_find() to avoid first snapshot
|
182
|
+
def Covers(file)
|
183
|
+
if cover?
|
184
|
+
#return if $".include?(file)
|
185
|
+
s = Snapshot.capture
|
186
|
+
if require(file)
|
187
|
+
z = Snapshot.capture
|
188
|
+
@coverage << (z - s)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
require file
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
def Helper(file)
|
197
|
+
local = File.join(File.dirname(caller[1]), file.to_str + '.rb')
|
198
|
+
if File.exist?(local)
|
199
|
+
require local
|
200
|
+
else
|
201
|
+
require file
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
#def Subtest(file)
|
207
|
+
# @subtest << file
|
208
|
+
# require file
|
209
|
+
#end
|
210
|
+
|
105
211
|
# Iterate through this suite's test cases.
|
106
212
|
def each(&block)
|
107
213
|
@testcases.each(&block)
|