lemon 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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