lemon 0.8.4 → 0.8.5

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.
Files changed (51) hide show
  1. data/.config/cucumber.yml +3 -0
  2. data/.gitignore +8 -0
  3. data/.reap/digest +678 -0
  4. data/.reap/test.reap +7 -0
  5. data/.ruby +42 -45
  6. data/Assembly +43 -0
  7. data/HISTORY.rdoc +10 -0
  8. data/MANIFEST +65 -0
  9. data/PROFILE +9 -6
  10. data/Rakefile +14 -0
  11. data/VERSION +1 -1
  12. data/lemon.gemspec +152 -0
  13. data/lib/lemon.yml +42 -45
  14. data/lib/lemon/cli.rb +4 -2
  15. data/lib/lemon/controller/test_runner.rb +4 -0
  16. data/lib/lemon/model/test_case.rb +122 -40
  17. data/notes/2010-05-05-coverage.rdoc +47 -0
  18. data/notes/2010-05-06-files_not_classes.rdoc +19 -0
  19. data/notes/2010-07-11-acid_testing.rdoc +52 -0
  20. data/notes/2010-08-02-enforcing-the-unit.md +68 -0
  21. data/notes/2010-08-03-new-api.md +37 -0
  22. data/site/.rsync-filter +8 -0
  23. data/site/assets/images/cut-lemon.png +0 -0
  24. data/site/assets/images/forkme.png +0 -0
  25. data/site/assets/images/github-logo.png +0 -0
  26. data/site/assets/images/lemon.jpg +0 -0
  27. data/site/assets/images/lemon.svg +39 -0
  28. data/site/assets/images/lemons-are-good.png +0 -0
  29. data/site/assets/images/opensource.png +0 -0
  30. data/site/assets/images/ruby-logo.png +0 -0
  31. data/site/assets/images/skin.jpg +0 -0
  32. data/site/assets/images/skin1.jpg +0 -0
  33. data/site/assets/images/tap.png +0 -0
  34. data/site/assets/images/title.png +0 -0
  35. data/site/assets/styles/class.css +6 -0
  36. data/site/assets/styles/reset.css +17 -0
  37. data/site/assets/styles/site.css +33 -0
  38. data/site/index.html +217 -0
  39. data/work/deprecated/command/abstract.rb +29 -0
  40. data/work/deprecated/command/coverage.rb +115 -0
  41. data/work/deprecated/command/generate.rb +124 -0
  42. data/work/deprecated/command/test.rb +112 -0
  43. data/work/reference/dsl2.rb +136 -0
  44. data/work/reference/dynamic_constant_lookup.rb +76 -0
  45. data/work/sandbox/lib/sample.rb +13 -0
  46. data/work/sandbox/test/sample_case.rb +12 -0
  47. data/work/trash/example-cover.rb +5 -0
  48. data/work/trash/example.rb +16 -0
  49. metadata +134 -101
  50. data/.yardopts +0 -7
  51. data/QED.rdoc +0 -1
@@ -61,7 +61,7 @@ module Lemon
61
61
  def test(scripts)
62
62
  require 'lemon/controller/test_runner'
63
63
 
64
- loadpath = options[:loadpath] || []
64
+ loadpath = options[:loadpath] || ['lib'] # + ['lib'] ?
65
65
  requires = options[:requires] || []
66
66
 
67
67
  loadpath.each{ |path| $LOAD_PATH.unshift(path) }
@@ -74,7 +74,9 @@ module Lemon
74
74
  scripts, :format=>options[:format], :namespaces=>options[:namespaces]
75
75
  )
76
76
 
77
- runner.run
77
+ success = runner.run
78
+
79
+ exit -1 unless success
78
80
  end
79
81
 
80
82
  #
@@ -77,6 +77,9 @@ module Lemon
77
77
  end
78
78
 
79
79
  # Run tests.
80
+ #
81
+ # @return [Boolean]
82
+ # Whether tests ran without error or failure.
80
83
  def run
81
84
  #prepare
82
85
  report.start_suite(suite)
@@ -128,6 +131,7 @@ module Lemon
128
131
  report.finish_case(testcase)
129
132
  end
130
133
  report.finish_suite(suite) #(successes, failures, errors, pendings)
134
+ return record[:error].size + record[:fail].size > 0 ? false : true
131
135
  end
132
136
 
133
137
  # Iterate over suite testcases, filtering out unselected testcases
@@ -21,11 +21,11 @@ module Lemon
21
21
  # Ordered list of testunits.
22
22
  attr :units
23
23
 
24
- # Before any test case units are run.
24
+ # Before matching test units.
25
25
  attr :before
26
26
  #attr_accessor :prepare
27
27
 
28
- # After all test case units are run.
28
+ # After matching test units.
29
29
  attr :after
30
30
  #attr_accessor :cleanup
31
31
 
@@ -88,6 +88,12 @@ module Lemon
88
88
  end
89
89
 
90
90
  # Define a unit test for this case.
91
+ #
92
+ # @example
93
+ # unit :puts => "print message with new line to stdout" do
94
+ # puts "Hello"
95
+ # end
96
+ #
91
97
  def unit(*target, &block)
92
98
  target = target.map{ |x| Hash === x ? x.to_a : x }.flatten
93
99
  method, aspect = *target
@@ -138,10 +144,15 @@ module Lemon
138
144
  end
139
145
  alias_method :omit, :Omit
140
146
 
147
+ # Setup is used to set things up for each unit test.
148
+ # The setup procedure is run before each unit.
141
149
  #
142
- def setup(description=nil, &block)
143
- if block
144
- context = TestContext.new(@testcase, description, &block)
150
+ # @param [String] description
151
+ # A brief description of what the setup procedure sets-up.
152
+ #
153
+ def setup(description=nil, &procedure)
154
+ if procedure
155
+ context = TestContext.new(@testcase, description, &procedure)
145
156
  @context = context
146
157
  #@function = false
147
158
  #@testcase.steps << context
@@ -150,60 +161,131 @@ module Lemon
150
161
  alias_method :Setup, :setup
151
162
  alias_method :Concern, :setup
152
163
  alias_method :concern, :setup
164
+
165
+ # @deprecate This alias will probably not stick around.
153
166
  alias_method :Context, :setup
154
167
  alias_method :context, :setup
155
168
 
169
+ =begin
170
+ # TODO: Currently there is no difference between Setup, Instance and Singleton
171
+
156
172
  ## Define a new test instance for this case.
157
- #def instance(description=nil, &block)
158
- # context = TestInstance.new(@testcase, description, &block)
159
- # @context = context
160
- # @function = false
161
- # #@testcase.steps << context
162
- #end
163
- #alias_method :Instance, :instance
173
+ def instance(description=nil, &block)
174
+ if block
175
+ #context = TestInstance.new(@testcase, description, &block)
176
+ context = TestContext.new(@testcase, description, &block)
177
+ else
178
+ context = TestContext.new(@testcase, description) do
179
+ @testcase.target.new # No arguments!!!
180
+ end
181
+ end
182
+ @context = context
183
+ #@function = false
184
+ #@testcase.steps << context
185
+ end
186
+ alias_method :Instance, :instance
164
187
 
165
188
  # Define a new test singleton for this case.
166
- #def Singleton(description=nil, &block)
167
- # context = TestSingleton.new(@testcase, description, &block)
168
- # @context = context
169
- # @function = true
170
- # #@testcase.steps << context
171
- #end
172
- #alias_method :singleton, :Singleton
189
+ def Singleton(description=nil, &block)
190
+ if block
191
+ #context = TestSingleton.new(@testcase, description, &block)
192
+ context = TestContext.new(@testcase, description, &block)
193
+ else
194
+ context = TestContext.new(@testcase, description){ @testcase.target }
195
+ end
196
+ @context = context
197
+ #@function = true
198
+ #@testcase.steps << context
199
+ end
200
+ alias_method :singleton, :Singleton
201
+ =end
173
202
 
174
- def teardown(&block)
175
- @context.teardown = block
203
+ # Teardown procedure is used to clean-up after each unit test.
204
+ def teardown(&procedure)
205
+ @context.teardown = procedure
176
206
  end
177
207
  alias_method :Teardown, :teardown
178
208
 
179
209
  # TODO: Make Before and After more generic to handle before and after
180
- # units, contexts/concerns, etc.
181
-
182
- # Define a before procedure for this case.
183
- def before(*matches, &block)
184
- @testcase.before[matches] = block
210
+ # units, contexts/concerns, etc?
211
+
212
+ # Define a _complex_ before procedure. The #before method allows
213
+ # before procedures to be defined that are triggered by a match
214
+ # against the unit's target method name or _aspect_ description.
215
+ # This allows groups of tests to be defined that share special
216
+ # setup code.
217
+ #
218
+ # @example
219
+ #
220
+ # unit :puts => "standard output (@stdout)" do
221
+ # puts "Hello"
222
+ # end
223
+ #
224
+ # before /@stdout/ do
225
+ # $stdout = StringIO.new
226
+ # end
227
+ #
228
+ # after /@stdout/ do
229
+ # $stdout = STDOUT
230
+ # end
231
+ #
232
+ # @param [Array<Symbol,Regexp>] matches
233
+ # List of match critera that must _all_ be matched
234
+ # to trigger the before procedure.
235
+ #
236
+ def before(*matches, &procedure)
237
+ @testcase.before[matches] = procedure
185
238
  end
186
239
  alias_method :Before, :before
187
240
 
188
- # Define an after procedure for this case.
189
- def after(*matches, &block)
190
- @testcase.after[matches] = block
241
+ # Define a _complex_ after procedure. The #before method allows
242
+ # before procedures to be defined that are triggered by a match
243
+ # against the unit's target method name or _aspect_ description.
244
+ # This allows groups of tests to be defined that share special
245
+ # teardown code.
246
+ #
247
+ # @example
248
+ #
249
+ # unit :puts => "standard output (@stdout)" do
250
+ # puts "Hello"
251
+ # end
252
+ #
253
+ # before /@stdout/ do
254
+ # $stdout = StringIO.new
255
+ # end
256
+ #
257
+ # after /@stdout/ do
258
+ # $stdout = STDOUT
259
+ # end
260
+ #
261
+ # @param [Array<Symbol,Regexp>] matches
262
+ # List of match critera that must _all_ be matched
263
+ # to trigger the after procedure.
264
+ #
265
+ def after(*matches, &procedure)
266
+ @testcase.after[matches] = procedure
191
267
  end
192
268
  alias_method :After, :after
193
269
 
194
- #
195
- #def prepare(&block)
196
- # @testcase.prepare = block
197
- #end
198
- #alias_method :Prepare, :prepare
270
+ # Define a "before all" procedure.
271
+ def prepare(&procedure)
272
+ before(&procedure)
273
+ end
274
+ alias_method :Prepare, :prepare
199
275
 
200
- #
201
- #def cleanup(&block)
202
- # @testcase.cleanup = block
203
- #end
204
- #alias_method :Cleanup, :cleanup
276
+ # Define an "after all" procedure.
277
+ def cleanup(&procedure)
278
+ after(&procedure)
279
+ end
280
+ alias_method :Cleanup, :cleanup
205
281
 
206
- # Load a helper script applicable to this test case.
282
+ # Load a helper script applicable to this test case. Unlike requiring
283
+ # a helper script, the #helper method will eval the file's contents
284
+ # directly into the test context (using instance_eval).
285
+ #
286
+ # @param [String] file
287
+ # File to eval into test context.
288
+ #
207
289
  def helper(file)
208
290
  instance_eval(File.read(file), file)
209
291
  end
@@ -0,0 +1,47 @@
1
+ = 2010-05-05 Getting Good Coverage
2
+
3
+ Getting good coverage analysis is not easy. The difficulty comes
4
+ is determining what is of interest to the tests and what
5
+ is not. That isn't so hard with completely new code, but
6
+ core extensions, for instance, are challenging to isolate
7
+ from original methods.
8
+
9
+ Thee issue can mostly be taken care of by taking a canonical
10
+ snapshot of the system before loading the tests. Then taking
11
+ a snapshot after loading the tests and comparing the two.
12
+ For the most part the difference will be the code in need of
13
+ coverage.
14
+
15
+ Unfortunately helper code still gets in the way of taking these
16
+ snapshots. As a result I have determined there are only four
17
+ possible complete solutions.
18
+
19
+ 1) Use a Ruby lexer/parser to syntatically deconstruct the
20
+ target libraries. This is a complex solution, and not one
21
+ I relish trying.
22
+
23
+ 2) All helpers must go in special files that are preloaded so
24
+ the snapshot can isolate it from the actual target code. This
25
+ means any mocks, for instance, would have to be defined in
26
+ separate files for the the test(s) that might use them. This
27
+ solution it good in that it actually encourges the writing
28
+ reusable helper code, but it can be annoying when writting
29
+ one-off mocks and such.
30
+
31
+ 3) As an alternate to #2, Lemon could provide a method
32
+ for specifying that a class/module or method is a helper
33
+ and can simply be ignored when analysing coverage.
34
+
35
+ 4) Lastly, the library file being covered can be specified
36
+ with a special method, e.g. #Covers. The method would act
37
+ just like +#require+ except that it takes a snapshot before and
38
+ after the loading the library, and in this way builds up
39
+ a table of proper coverage targets.
40
+
41
+ Options #2 and #3 have proven lack luster. It's simply too
42
+ much overhead involved to get good coverage. Option #4 is the most
43
+ robust choice. Unfortunately coverage reports can be very slow
44
+ if there are lot of files to cover --Snapshots can take a second
45
+ or two to create. Hopefully it can be sped up with refinement,
46
+ but at the very least the coverage reporting if optional.
47
+
@@ -0,0 +1,19 @@
1
+ = 2010-05-06 Files, Not Classes
2
+
3
+ Just had a realization about my approch to Lemon tests.
4
+ Until now I have equated the test-case to the class/module
5
+ and the test-unit to the method. That makes sense, but it means
6
+ I have left out a very important factor in the the design
7
+ of tests: the file.
8
+
9
+ I had defined a test-suite as the entire set of tests to
10
+ be run. But I think now I am mistaken. The suite should
11
+ correspond to the fiel being tested. Often that will mean
12
+ one test-case per test-suite, but that's okay.
13
+
14
+ After the next release, 0.7.0, I will implement this
15
+ refactorization. I beleive it should ultimately help
16
+ improve test coverage as well.
17
+
18
+
19
+
@@ -0,0 +1,52 @@
1
+ = 2010-07-11 | Kool-Aid
2
+
3
+ Taking Unit Testing to the next level with a dab of DBC.
4
+
5
+ Say we have,
6
+
7
+ class K
8
+
9
+ def f1(a1)
10
+ g1(a1)
11
+ end
12
+
13
+ def g1(a1)
14
+ a1 + 1
15
+ end
16
+
17
+ end
18
+
19
+ Now what do we want to say about K#f1?
20
+
21
+ unit K, :f1 do |a|
22
+ case a
23
+ when 1
24
+ result do |r|
25
+ r.assert == 2
26
+ end
27
+ end
28
+ end
29
+
30
+ Now later we induce labor.
31
+
32
+ k = K.new
33
+ k.f1(1)
34
+
35
+ It does't really matter what the result class contains, or how it
36
+ even gets called. Thus it can test side-effects as well as simple
37
+ functional results.
38
+
39
+ Okay, so how do we implement this?
40
+
41
+ class K
42
+
43
+ alias "f1:lemon", :f1
44
+
45
+ def f1(a1)
46
+ Lemon.verify_unit(binding) do
47
+ send("f1:lemon", a1)
48
+ end
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,68 @@
1
+ 2010-08-02 | Enforcing the Unit
2
+
3
+ The current API for creating a Lemon unit testcase looks like this:
4
+
5
+ covers 'someclass'
6
+
7
+ testcase SomeClass do
8
+
9
+ unit :some_method => "does something or another" do
10
+ # ...
11
+ end
12
+
13
+ end
14
+
15
+ As it currentlt standa the current design is little more than a means of
16
+ organization, orienting the developer to think in terms of test units.
17
+ What is does not do is enforce the actual testing the the unit referenced.
18
+ We could put any old mess in the unit block and as long as it did not raise
19
+ an exception, it would get a *pass*.
20
+
21
+ Taking some time to consider this in depth, I've concieved of a way in which
22
+ that use of the method could in fact be enforced.
23
+
24
+ covers 'someclass'
25
+
26
+ testcase SomeClass do
27
+
28
+ setup do
29
+ SomeClass.new
30
+ end
31
+
32
+ unit :some_method => "does something or another" do |unit|
33
+ unit.object # object from setup
34
+ unit.call(...) # calls #some_method on unit.object
35
+ end
36
+
37
+ end
38
+
39
+ What is intersting about this, beyond that fact that it enforces the use of
40
+ the class or module and method involved, but that it also does so in
41
+ a way naturally suited to mocking --the `unit` delegator could even have
42
+ mocking methods built-in.
43
+
44
+ unit :some_method => "does something or another" do |unit|
45
+ unit.receives.foo(:bar) # object from setup would receive this call
46
+ unit.returns(:baz) # the subsequent #call will return this
47
+ unit.call(...) # calls #some_method on unit.object
48
+ end
49
+
50
+ On the downside this approach limits what can be done in the unit block.
51
+ One _has_ to utilize the object as defined in `setup` and one _has_ to invoke
52
+ the unit method via the `#call` interface. Though, I suppose one could argue
53
+ that these limitations are a good thing, as they help the unit stay narrowly
54
+ focused on that goal at hand.
55
+
56
+ I think this approach is worth considering for a possible furture version.
57
+ Perhaps a "Lemon 2.0". For the time being I believe we can enforce the unit
58
+ without resorting this major API change.
59
+
60
+ The next release of Lemon will temporarily override the unit method on the
61
+ target class for each unit execution. If the unit method gets called within
62
+ the unit block, then it will be noted by the overridden method before passing
63
+ off to the original definition. The approach is perhaps a bit draconian, and
64
+ is certainly only possible thanks to the remarkable dynamicism of Ruby, but
65
+ it should work perfectly well. So now, if the target method is not called within
66
+ the taget block, the unit will raise a Pending exception, regardless of the
67
+ code in the block. Unit Enforced!
68
+
@@ -0,0 +1,37 @@
1
+ # 2010-08-03 | A New API
2
+
3
+ Simplified API. There is one main method.
4
+
5
+ unit_test SomeClass, :some_method, "description" do
6
+ # test ...
7
+ end
8
+
9
+ It will be a global method. The block notations would still work, but
10
+ they would simple become wrappers for the main method.
11
+
12
+ testcase SomeClass do
13
+
14
+ unit :some_method, "description" do
15
+
16
+ end
17
+
18
+ end
19
+
20
+ If I can make it backward compatible, I may also allow something like:
21
+
22
+ testcase SomeClass do
23
+
24
+ unit :some_method do
25
+
26
+ concern "description" do
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ By using this global method, I should be able to simplify the underlying
35
+ implementation a great deal, which has been major concern about Lemon
36
+ as of late.
37
+