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.
@@ -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
@@ -50,8 +50,9 @@ module Lemon::Test
50
50
  end
51
51
 
52
52
  # Define a new test concern for this case.
53
- def Concern(*description)
54
- concern = Concern.new(self, description)
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(match=nil, &block)
87
- @before_clauses[match] = block #<< Advice.new(match, &block)
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(match=nil, &block)
93
- @after_clauses[match] = block #<< Advice.new(match, &block)
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
- raise PendingAssertion
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 PendingAssertion < Assertion
136
+ class Pending < Assertion
137
+ def self.to_proc; lambda{ raise self }; end
116
138
  end
117
139
 
@@ -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
@@ -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
- def initialize(*files)
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
- loadfiles(*files)
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 loadfiles(*files)
35
- Lemon.suite = self
62
+ def cover?
63
+ @options[:cover]
64
+ end
36
65
 
37
- # directories glob *.rb files
38
- files = files.flatten.map do |file|
39
- if File.directory?(file)
40
- Dir[File.join(file, '**', '*.rb')]
41
- else
42
- file
43
- end
44
- end.flatten.uniq
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
- files.each do |file|
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
- # Load a helper. This method must be used when loading local
56
- # suite support. The usual #require or #load can only be used
57
- # for extenal support libraries (such as a test mock framework).
58
- # This is so because suite code is not evaluated at the toplevel.
59
- def helper(file)
60
- instance_eval(File.read(file), file)
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)