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
@@ -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)
|