lemon 0.8.1 → 0.8.2

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 (79) hide show
  1. data/HISTORY.rdoc +15 -0
  2. data/README.rdoc +32 -14
  3. data/bin/lemon +3 -2
  4. data/demo/case_example_fail.rb +15 -0
  5. data/demo/case_example_pass.rb +32 -0
  6. data/demo/case_example_pending.rb +14 -0
  7. data/demo/case_example_untested.rb +10 -0
  8. data/demo/fixture/example-use.rb +5 -0
  9. data/demo/fixture/example.rb +20 -0
  10. data/lib/lemon.rb +2 -2
  11. data/lib/lemon/cli.rb +281 -0
  12. data/lib/lemon/controller/coverage_analyzer.rb +343 -0
  13. data/lib/lemon/controller/scaffold_generator.rb +110 -0
  14. data/lib/lemon/controller/test_runner.rb +284 -0
  15. data/lib/lemon/meta/data.rb +29 -0
  16. data/lib/lemon/meta/gemfile +24 -0
  17. data/{PROFILE → lib/lemon/meta/profile} +6 -5
  18. data/lib/lemon/model/ae.rb +4 -0
  19. data/lib/lemon/model/cover_unit.rb +75 -0
  20. data/lib/lemon/{dsl.rb → model/main.rb} +22 -28
  21. data/lib/lemon/model/pending.rb +10 -0
  22. data/lib/lemon/model/snapshot.rb +203 -0
  23. data/lib/lemon/model/source_parser.rb +198 -0
  24. data/lib/lemon/model/test_case.rb +221 -0
  25. data/lib/lemon/model/test_context.rb +90 -0
  26. data/lib/lemon/model/test_suite.rb +216 -0
  27. data/lib/lemon/{test/unit.rb → model/test_unit.rb} +40 -28
  28. data/lib/lemon/{coversheet → view/cover_reports}/abstract.rb +19 -20
  29. data/lib/lemon/view/cover_reports/compact.rb +37 -0
  30. data/lib/lemon/view/cover_reports/outline.rb +45 -0
  31. data/lib/lemon/view/cover_reports/verbose.rb +51 -0
  32. data/lib/lemon/view/cover_reports/yaml.rb +15 -0
  33. data/lib/lemon/view/test_reports/abstract.rb +149 -0
  34. data/lib/lemon/view/test_reports/dotprogress.rb +73 -0
  35. data/lib/lemon/view/test_reports/html.rb +146 -0
  36. data/lib/lemon/view/test_reports/outline.rb +118 -0
  37. data/lib/lemon/view/test_reports/summary.rb +131 -0
  38. data/lib/lemon/view/test_reports/tap.rb +49 -0
  39. data/lib/lemon/view/test_reports/verbose.rb +197 -0
  40. data/meta/data.rb +29 -0
  41. data/meta/gemfile +24 -0
  42. data/meta/profile +17 -0
  43. data/test/api/applique/fs.rb +18 -0
  44. data/test/api/coverage/complete.rdoc +136 -0
  45. data/test/api/coverage/extensions.rdoc +61 -0
  46. data/test/api/coverage/incomplete.rdoc +97 -0
  47. data/{features → test/cli}/coverage.feature +4 -4
  48. data/{features → test/cli}/generate.feature +2 -2
  49. data/{features → test/cli}/step_definitions/coverage_steps.rb +0 -0
  50. data/{features → test/cli}/support/ae.rb +0 -0
  51. data/{features → test/cli}/support/aruba.rb +0 -0
  52. data/{features → test/cli}/test.feature +0 -0
  53. data/test/fixtures/case_complete.rb +17 -4
  54. data/test/fixtures/case_inclusion.rb +18 -0
  55. data/test/fixtures/case_incomplete.rb +4 -4
  56. data/test/fixtures/example.rb +5 -0
  57. data/test/fixtures/helper.rb +13 -0
  58. data/test/runner +3 -0
  59. data/test/unit/case_coverage_analyzer.rb +25 -0
  60. data/test/unit/case_test_case_dsl.rb +46 -0
  61. metadata +87 -42
  62. data/REQUIRE +0 -9
  63. data/VERSION +0 -6
  64. data/lib/lemon/command.rb +0 -184
  65. data/lib/lemon/coverage.rb +0 -260
  66. data/lib/lemon/coversheet/outline.rb +0 -47
  67. data/lib/lemon/kernel.rb +0 -24
  68. data/lib/lemon/reporter.rb +0 -22
  69. data/lib/lemon/reporter/abstract.rb +0 -97
  70. data/lib/lemon/reporter/dotprogress.rb +0 -68
  71. data/lib/lemon/reporter/outline.rb +0 -105
  72. data/lib/lemon/reporter/verbose.rb +0 -143
  73. data/lib/lemon/runner.rb +0 -308
  74. data/lib/lemon/snapshot.rb +0 -185
  75. data/lib/lemon/test/case.rb +0 -139
  76. data/lib/lemon/test/concern.rb +0 -52
  77. data/lib/lemon/test/suite.rb +0 -229
  78. data/test/case_coverage.rb +0 -26
  79. data/test/case_testcase.rb +0 -58
@@ -0,0 +1,284 @@
1
+ module Lemon
2
+
3
+ require 'lemon/model/main'
4
+ require 'lemon/model/test_suite'
5
+
6
+ # The TestRunner class handles the execution of Lemon tests.
7
+ class TestRunner
8
+
9
+ # Test suite to run.
10
+ attr :suite
11
+
12
+ #
13
+ attr :files
14
+
15
+ # Report format.
16
+ attr :format
17
+
18
+ # Record pass, fail, error, pending and omitted units.
19
+ attr :record
20
+
21
+ # New Runner.
22
+ def initialize(files, options={})
23
+ @files = files
24
+ @options = options
25
+
26
+ @record = {:pass=>[], :fail=>[], :error=>[], :pending=>[], :omit=>[]}
27
+
28
+ ## TODO: can we create and assign the suite here?
29
+ @suite = Lemon::TestSuite.new(files) #([])
30
+ #@suite = Lemon.suite
31
+
32
+ initialize_rc # TODO: before or after @suite =
33
+
34
+ #files = files.map{ |f| Dir[f] }.flatten
35
+ #files = files.map{ |f|
36
+ # if File.directory?(f)
37
+ # Dir[File.join(f, '**/*.rb')]
38
+ # else
39
+ # f
40
+ # end
41
+ #}.flatten.uniq
42
+ #files = files.map{ |f| File.expand_path(f) }
43
+ #files.each{ |s| require s }
44
+ end
45
+
46
+ #
47
+ def initialize_rc
48
+ if file = Dir['./{.,}config/lemon/rc.rb'].first
49
+ require file
50
+ end
51
+ end
52
+
53
+ #
54
+ def format
55
+ @options[:format]
56
+ end
57
+
58
+ #
59
+ #def cover?
60
+ # @options[:cover]
61
+ #end
62
+
63
+ # Namespaces option specifies the selection of test cases
64
+ # to run. Is is an array of strings which are matched
65
+ # against the module/class names using #start_wtih?
66
+ def namespaces
67
+ @options[:namespaces] || []
68
+ end
69
+
70
+ # Run tests.
71
+ def run
72
+ #prepare
73
+ report.start_suite(suite)
74
+ each do |testcase|
75
+ scope = Object.new
76
+ scope.extend(testcase.dsl)
77
+ report.start_case(testcase)
78
+ if testcase.prepare #before[[]]
79
+ scope.instance_eval(&testcase.prepare)
80
+ end
81
+ testcase.each do |unit|
82
+ #case step
83
+ #when TestInstance
84
+ # reporter.report_instance(step)
85
+ #when TestUnit
86
+ # unit = step
87
+ if unit.omit?
88
+ report.omit(unit)
89
+ record[:omit] << unit
90
+ next
91
+ end
92
+ report.start_unit(unit)
93
+ run_pretest_procedures(unit, scope) #, suite, testcase)
94
+ begin
95
+ run_unit(unit, scope)
96
+ #unit.call(scope)
97
+ report.pass(unit)
98
+ record[:pass] << unit
99
+ rescue Pending => exception
100
+ exception = clean_backtrace(exception)
101
+ report.pending(unit, exception)
102
+ record[:pending] << [unit, exception]
103
+ rescue Assertion => exception
104
+ exception = clean_backtrace(exception)
105
+ report.fail(unit, exception)
106
+ record[:fail] << [unit, exception]
107
+ rescue Exception => exception
108
+ exception = clean_backtrace(exception)
109
+ report.error(unit, exception)
110
+ record[:error] << [unit, exception]
111
+ end
112
+ report.finish_unit(unit)
113
+ run_postest_procedures(unit, scope) #, suite, testcase)
114
+ #end
115
+ end
116
+ if testcase.cleanup #after[[]]
117
+ scope.instance_eval(&testcase.cleanup)
118
+ end
119
+ report.finish_case(testcase)
120
+ end
121
+ report.finish_suite(suite) #(successes, failures, errors, pendings)
122
+ end
123
+
124
+ # Iterate over suite testcases, filtering out unselected testcases
125
+ # if any namespaces are provided.
126
+ def each(&block)
127
+ if namespaces.empty?
128
+ suite.each do |testcase|
129
+ block.call(testcase)
130
+ end
131
+ else
132
+ suite.each do |testcase|
133
+ next unless namespaces.any?{ |n| testcase.target.name.start_with?(n) }
134
+ block.call(testcase)
135
+ end
136
+ end
137
+ end
138
+
139
+ # All output is handled by a reporter.
140
+ def report
141
+ @report ||= report_find(format)
142
+ end
143
+
144
+ # Find a report type be name fragment.
145
+ def report_find(format)
146
+ format = format ? format.to_s.downcase : 'dotprogress'
147
+ format = report_list.find do |r|
148
+ /^#{format}/ =~ r
149
+ end
150
+ raise "unsupported format" unless format
151
+ require "lemon/view/test_reports/#{format}"
152
+ reporter = Lemon::TestReports.const_get(format.capitalize)
153
+ reporter.new(self)
154
+ end
155
+
156
+ # Returns a list of report types.
157
+ def report_list
158
+ Dir[File.dirname(__FILE__) + '/../view/test_reports/*.rb'].map do |rb|
159
+ File.basename(rb).chomp('.rb')
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ #
166
+ def run_unit(unit, scope)
167
+ if unit.function?
168
+ base = (class << unit.testcase.target; self; end)
169
+ else
170
+ base = unit.testcase.target
171
+ end
172
+
173
+ raise Pending unless unit.procedure
174
+
175
+ begin
176
+ base.class_eval do
177
+ alias_method "_lemon_#{unit.target}", unit.target
178
+ define_method(unit.target) do |*a,&b|
179
+ unit.tested = true
180
+ __send__("_lemon_#{unit.target}",*a,&b)
181
+ end
182
+ end
183
+ rescue => error
184
+ Kernel.eval %[raise #{error.class}, "#{unit.target} not tested"], unit.procedure
185
+ end
186
+ #Lemon.test_stack << self # hack
187
+
188
+ begin
189
+ if unit.context && unit.procedure.arity != 0
190
+ cntx = unit.context.setup(scope)
191
+ scope.instance_exec(cntx, &unit.procedure) #procedure.call
192
+ else
193
+ scope.instance_exec(&unit.procedure) #procedure.call
194
+ end
195
+ unit.context.teardown(scope) if unit.context
196
+ ensure
197
+ #Lemon.test_stack.pop
198
+ base.class_eval %{
199
+ alias_method "#{unit.target}", "_lemon_#{unit.target}"
200
+ }
201
+ end
202
+ if !unit.tested
203
+ #exception = Untested.new("#{unit.target} not tested")
204
+ if RUBY_VERSION < '1.9'
205
+ Kernel.eval %[raise Pending, "#{unit.target} not tested"], unit.procedure
206
+ else
207
+ Kernel.eval %[raise Pending, "#{unit.target} not tested"], unit.procedure.binding
208
+ end
209
+ end
210
+ end
211
+
212
+ =begin
213
+ #
214
+ def run_concern_procedures(concern, scope) #suite, testcase)
215
+ tc = concern.testcase
216
+ suite = tc.suite
217
+ suite.when_clauses.each do |match, block|
218
+ if match.nil? or match === concern.to_s
219
+ #block.call #(test_case)
220
+ scope.instance_exec(tc, &block)
221
+ end
222
+ end
223
+ tc.when_clauses.each do |match, block|
224
+ if match.nil? or match === concern.to_s
225
+ if match === concern.to_s
226
+ #block.call #(test_case)
227
+ scope.instance_exec(tc, &block)
228
+ end
229
+ end
230
+ end
231
+ concern.call(scope)
232
+ end
233
+ =end
234
+
235
+ # Run pre-test advice.
236
+ def run_pretest_procedures(unit, scope) #, suite, testcase)
237
+ suite = unit.testcase.suite
238
+ suite.before.each do |matches, block|
239
+ if matches.all?{ |match| unit.match?(match) }
240
+ scope.instance_exec(unit, &block) #block.call(unit)
241
+ end
242
+ end
243
+ unit.testcase.before.each do |matches, block|
244
+ if matches.all?{ |match| unit.match?(match) }
245
+ scope.instance_exec(unit, &block) #block.call(unit)
246
+ end
247
+ end
248
+ end
249
+
250
+ # Run post-test advice.
251
+ def run_postest_procedures(unit, scope) #, suite, testcase)
252
+ suite = unit.testcase.suite
253
+ unit.testcase.after.each do |matches, block|
254
+ if matches.all?{ |match| unit.match?(match) }
255
+ scope.instance_exec(unit, &block) #block.call(unit)
256
+ end
257
+ end
258
+ suite.after.each do |matches, block|
259
+ if matches.all?{ |match| unit.match?(match) }
260
+ scope.instance_exec(unit, &block) #block.call(unit)
261
+ end
262
+ end
263
+ end
264
+
265
+ EXCLUDE = Regexp.new(Regexp.escape(File.dirname(File.dirname(__FILE__))))
266
+
267
+ # Remove reference to lemon library from backtrace.
268
+ # TODO: Matching `bin/lemon` is not robust.
269
+ def clean_backtrace(exception)
270
+ trace = exception.backtrace
271
+ trace = trace.reject{ |t| t =~ /bin\/lemon/ }
272
+ trace = trace.reject{ |t| t =~ EXCLUDE }
273
+ if trace.empty?
274
+ exception
275
+ else
276
+ exception.set_backtrace(trace)
277
+ exception
278
+ end
279
+ end
280
+
281
+ end
282
+
283
+ end
284
+
@@ -0,0 +1,29 @@
1
+ Object.__send__(:remove_const, :VERSION) if Object.const_defined?(:VERSION) # becuase Ruby 1.8~ gets in the way
2
+
3
+ module Lemon
4
+
5
+ def self.__DIR__
6
+ File.dirname(__FILE__)
7
+ end
8
+
9
+ def self.gemfile
10
+ @gemfile ||= (
11
+ require 'yaml'
12
+ YAML.load(File.new(__DIR__ + '/gemfile'))
13
+ )
14
+ end
15
+
16
+ def self.profile
17
+ @profile ||= (
18
+ require 'yaml'
19
+ YAML.load(File.new(__DIR__ + '/profile'))
20
+ )
21
+ end
22
+
23
+ def self.const_missing(name)
24
+ key = name.to_s.downcase
25
+ gemfile[key] || profile[key] || super(name)
26
+ end
27
+
28
+ end
29
+
@@ -0,0 +1,24 @@
1
+ ---
2
+ name : lemon
3
+ version : 0.8.2
4
+ date : 2010-09-05
5
+
6
+ requires:
7
+ - name: ae
8
+ group: runtime
9
+
10
+ - name: syckle
11
+ group: development
12
+
13
+ - name: box
14
+ group: development
15
+
16
+ - name: cucumber
17
+ group: test
18
+
19
+ - name: ae
20
+ group: test
21
+
22
+ - name: aruba
23
+ group: test
24
+
@@ -1,11 +1,10 @@
1
1
  ---
2
- title: Lemon
3
- suite: proutils
4
- copyright: Copyright 2009 Thomas Sawyer
5
- license: MIT
2
+ title : Lemon
3
+ suite : proutils
6
4
  summary: Pucker-tight Unit Testing
7
5
  authors: Thomas Sawyer
8
- contact: proutils@googlegroups.com
6
+ contact: trans <transfire@gmail.com>
7
+ license: Apache 2.0
9
8
 
10
9
  description:
11
10
  Lemon is a unit testing framework that tightly correlates
@@ -14,3 +13,5 @@ description:
14
13
  resources:
15
14
  homepage: http://proutils.github.com/lemon
16
15
  repository: git://github.com/proutils/lemon.git
16
+
17
+ copyright: Copyright 2009 Thomas Sawyer
@@ -0,0 +1,4 @@
1
+ require 'ae'
2
+ require 'ae/expect'
3
+ require 'ae/should'
4
+ require 'ae/pry'
@@ -0,0 +1,75 @@
1
+ module Lemon
2
+
3
+ # Unit of coverage, ecapsulates a method, it's characteristics and a flag
4
+ # as to whether it has been covered or not.
5
+ class CoverUnit
6
+
7
+ attr :target
8
+ attr :method
9
+ attr :function
10
+
11
+ def initialize(target, method, props={})
12
+ @target = target
13
+ @method = method.to_sym
14
+ @function = props[:function] ? true : false
15
+ @covered = props[:covered]
16
+
17
+ if @function
18
+ @private = !@target.public_methods.find{ |m| m.to_sym == @method }
19
+ else
20
+ @private = !@target.public_instance_methods.find{ |m| m.to_sym == @method }
21
+ end
22
+ end
23
+
24
+ # Method access is private or protected?
25
+ def private?
26
+ @private
27
+ end
28
+
29
+ # Marked as covered?
30
+ def covered?
31
+ @covered
32
+ end
33
+
34
+ #
35
+ def function?
36
+ @function
37
+ end
38
+
39
+ #
40
+ def hash
41
+ @target.hash ^ @method.hash ^ @function.hash
42
+ end
43
+
44
+ #
45
+ def to_s
46
+ if @function
47
+ "#{@target}.#{@method}"
48
+ else
49
+ "#{@target}##{@method}"
50
+ end
51
+ end
52
+ alias to_str to_s
53
+
54
+ def eql?(other)
55
+ return false unless Unit === other
56
+ return false unless target == other.target
57
+ return false unless method == other.method
58
+ return false unless function == other.function
59
+ return true
60
+ end
61
+
62
+ def inspect
63
+ "#{target}#{function ? '.' : '#'}#{method}"
64
+ end
65
+
66
+ def <=>(other)
67
+ c = (target.name <=> other.target.name)
68
+ return c unless c == 0
69
+ return -1 if function && !other.function
70
+ return 1 if !function && other.function
71
+ method.to_s <=> other.method.to_s
72
+ end
73
+ end
74
+
75
+ end
@@ -1,49 +1,42 @@
1
- # Current suite being defined. This is used
2
- # to define a Suite object via the toplevel DSL.
3
- def Lemon.suite
4
- @suite
5
- end
6
-
7
- #
8
- def Lemon.suite=(suite)
9
- @suite = suite
10
- end
1
+ # NOTE: This code is not being used. It is here for the time being
2
+ # on the outseide change that I decide to go back to a toplevel design.
11
3
 
12
- #
13
- def Before(match=nil, &block)
14
- Lemon.suite.Before(match, &block)
15
- end
4
+ require 'lemon/model/test_suite'
16
5
 
17
6
  #
18
- def After(match=nil, &block)
19
- Lemon.suite.After(match, &block)
20
- end
7
+ #def Before(match=nil, &block)
8
+ # Lemon.suite.Before(match, &block)
9
+ #end
21
10
 
22
11
  #
23
- def When(match=nil, &block)
24
- Lemon.suite.When(match, &block)
25
- end
12
+ #def After(match=nil, &block)
13
+ # Lemon.suite.After(match, &block)
14
+ #end
26
15
 
27
16
  #
28
- def Case(target_class, &block)
29
- Lemon.suite.Case(target_class, &block)
17
+ def testcase(target_class, &block)
18
+ Lemon.suite.dsl.testcase(target_class, &block)
30
19
  end
31
- alias :TestCase :Case
20
+ alias :TestCase :testcase
21
+ alias :Case :testcase
22
+ alias :tests :testcase # can't use test b/c of kernel method
32
23
 
33
24
  #
34
- def Covers(script)
35
- Lemon.suite.Covers(script)
25
+ def covers(script)
26
+ Lemon.suite.dsl.covers(script)
36
27
  end
28
+ alias :Covers :covers
37
29
 
38
30
  #
39
- def Helper(script)
40
- Lemon.suite.Helper(script)
41
- end
31
+ #def Helper(script)
32
+ # Lemon.suite.Helper(script)
33
+ #end
42
34
 
43
35
  #def Subtest(script)
44
36
  # Lemon.suite.Subtest(script)
45
37
  #end
46
38
 
39
+ =begin
47
40
  # FIXME: This is a BIG FAT HACK! For the life of me I cannot find
48
41
  # a way to resolve module constants included in the test cases.
49
42
  # Because of closure, the constant lookup goes through here, and not
@@ -79,4 +72,5 @@ end
79
72
  def Lemon.test_stack
80
73
  @@test_stack ||= []
81
74
  end
75
+ =end
82
76