lemon 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/.ruby +23 -14
  2. data/.yardopts +6 -0
  3. data/Config.rb +14 -0
  4. data/{HISTORY.rdoc → HISTORY.md} +26 -11
  5. data/LICENSE.txt +27 -0
  6. data/README.md +42 -28
  7. data/SPECSHEET.md +314 -0
  8. data/bin/{lemonade → lemons} +0 -0
  9. data/lib/lemon.yml +23 -14
  10. data/lib/lemon/cli.rb +19 -8
  11. data/lib/lemon/cli/base.rb +50 -20
  12. data/lib/lemon/cli/generate.rb +51 -16
  13. data/lib/lemon/cli/lemon.ascii +84 -0
  14. data/lib/lemon/cli/obrother.rb +35 -0
  15. data/lib/lemon/cli/scaffold.rb +116 -0
  16. data/lib/lemon/core_ext.rb +2 -2
  17. data/lib/lemon/core_ext/module.rb +9 -0
  18. data/lib/lemon/coverage/analyzer.rb +76 -5
  19. data/lib/lemon/coverage/cover_unit.rb +38 -14
  20. data/lib/lemon/coverage/formats/verbose.rb +1 -1
  21. data/lib/lemon/coverage/generator.rb +196 -0
  22. data/lib/lemon/coverage/snapshot.rb +16 -16
  23. data/lib/lemon/coverage/source_parser.rb +103 -37
  24. data/lib/lemon/ignore_callers.rb +19 -0
  25. data/lib/lemon/test_case.rb +135 -26
  26. data/lib/lemon/test_class.rb +16 -3
  27. data/lib/lemon/test_class_method.rb +58 -0
  28. data/lib/lemon/test_method.rb +57 -68
  29. data/lib/lemon/test_module.rb +47 -44
  30. data/lib/lemon/test_proc.rb +28 -2
  31. data/lib/lemon/test_scope.rb +14 -0
  32. data/lib/lemon/test_setup.rb +1 -1
  33. data/lib/lemon/test_world.rb +7 -0
  34. data/{work/deprecated/features/support → spec/applique}/ae.rb +0 -0
  35. data/spec/coverage/{01_complete.rdoc → 01_complete.md} +3 -3
  36. data/spec/coverage/{02_incomplete.rdoc → 02_incomplete.md} +2 -2
  37. data/spec/coverage/{03_extensions.rdoc → 03_extensions.md} +2 -2
  38. data/try/case_scope.rb +19 -0
  39. metadata +50 -102
  40. data/.gemspec +0 -152
  41. data/.gitignore +0 -8
  42. data/.reap/digest +0 -678
  43. data/.reap/test.reap +0 -7
  44. data/Assembly +0 -37
  45. data/COPYING.rdoc +0 -33
  46. data/MANIFEST +0 -55
  47. data/PROFILE +0 -30
  48. data/Rakefile +0 -23
  49. data/VERSION +0 -1
  50. data/lib/lemon/core_ext/omission.rb +0 -18
  51. data/lib/lemon/generator.rb +0 -149
  52. data/notes/2010-05-05-coverage.rdoc +0 -47
  53. data/notes/2010-05-06-files-not-classes.rdoc +0 -19
  54. data/notes/2010-07-11-acid-testing.rdoc +0 -52
  55. data/notes/2010-08-02-enforcing-the-unit.md +0 -68
  56. data/notes/2010-08-03-new-api.md +0 -37
  57. data/notes/2011-07-07-nailing-down-the-nomenclature.md +0 -6
  58. data/site/.rsync-filter +0 -8
  59. data/site/assets/images/cut-lemon.png +0 -0
  60. data/site/assets/images/forkme.png +0 -0
  61. data/site/assets/images/github-logo.png +0 -0
  62. data/site/assets/images/lemon.jpg +0 -0
  63. data/site/assets/images/lemon.svg +0 -39
  64. data/site/assets/images/lemons-are-good.png +0 -0
  65. data/site/assets/images/opensource.png +0 -0
  66. data/site/assets/images/ruby-logo.png +0 -0
  67. data/site/assets/images/skin.jpg +0 -0
  68. data/site/assets/images/skin1.jpg +0 -0
  69. data/site/assets/images/tap.png +0 -0
  70. data/site/assets/images/title.png +0 -0
  71. data/site/assets/styles/class.css +0 -6
  72. data/site/assets/styles/reset.css +0 -17
  73. data/site/assets/styles/site.css +0 -33
  74. data/site/index.html +0 -218
  75. data/work/deprecated/command/abstract.rb +0 -29
  76. data/work/deprecated/command/coverage.rb +0 -115
  77. data/work/deprecated/command/generate.rb +0 -124
  78. data/work/deprecated/command/test.rb +0 -112
  79. data/work/deprecated/cucumber.yml +0 -3
  80. data/work/deprecated/features/coverage.feature +0 -65
  81. data/work/deprecated/features/generate.feature +0 -66
  82. data/work/deprecated/features/step_definitions/coverage_steps.rb +0 -1
  83. data/work/deprecated/features/support/aruba.rb +0 -1
  84. data/work/deprecated/features/test.feature +0 -67
  85. data/work/deprecated/model/dsl/advice.rb +0 -78
  86. data/work/deprecated/model/dsl/subject.rb +0 -40
  87. data/work/deprecated/model/main.rb +0 -87
  88. data/work/deprecated/model/test.rb +0 -54
  89. data/work/deprecated/model/test_base_dsl.rb +0 -88
  90. data/work/deprecated/model/test_clause.rb +0 -112
  91. data/work/deprecated/model/test_context.rb +0 -90
  92. data/work/deprecated/model/test_feature.rb +0 -128
  93. data/work/deprecated/model/test_scenario.rb +0 -137
  94. data/work/deprecated/model/test_suite.rb +0 -297
  95. data/work/deprecated/rake.rb +0 -103
  96. data/work/deprecated/test/case_coverage_analyzer.rb +0 -25
  97. data/work/deprecated/test/case_test_case_dsl.rb +0 -46
  98. data/work/deprecated/test/fixtures/case_complete.rb +0 -25
  99. data/work/deprecated/test/fixtures/case_inclusion.rb +0 -18
  100. data/work/deprecated/test/fixtures/case_incomplete.rb +0 -12
  101. data/work/deprecated/test/fixtures/example.rb +0 -13
  102. data/work/deprecated/test/fixtures/helper.rb +0 -13
  103. data/work/deprecated/test/runner +0 -2
  104. data/work/old-tests/case_example.rb +0 -15
  105. data/work/old-tests/feature_example.rb +0 -40
  106. data/work/reference/dsl2.rb +0 -140
  107. data/work/reference/dynamic_constant_lookup.rb +0 -76
@@ -0,0 +1,19 @@
1
+ module Lemon
2
+
3
+ #
4
+ def self.ignore_callers
5
+ ignore_path = File.expand_path(File.join(__FILE__, '../../..'))
6
+ ignore_regexp = Regexp.new(Regexp.escape(ignore_path))
7
+ [ ignore_regexp, /bin\/lemon/ ]
8
+ end
9
+
10
+ #
11
+ def self.setup_ignore_callers
12
+ $RUBY_IGNORE_CALLERS ||= []
13
+ $RUBY_IGNORE_CALLERS.concat(ignore_callers)
14
+ end
15
+
16
+ end
17
+
18
+ #
19
+ Lemon.setup_ignore_callers
@@ -2,6 +2,7 @@ require 'lemon/core_ext'
2
2
  require 'lemon/test_advice'
3
3
  require 'lemon/test_setup'
4
4
  require 'lemon/test_world'
5
+ require 'lemon/test_scope'
5
6
 
6
7
  module Lemon
7
8
 
@@ -32,6 +33,7 @@ module Lemon
32
33
  # Skip execution of test case?
33
34
  attr :skip
34
35
 
36
+ #
35
37
  # A test case +target+ is a class or module.
36
38
  #
37
39
  # @param [Hash] settings
@@ -59,58 +61,88 @@ module Lemon
59
61
  @label = settings[:label]
60
62
  @setup = settings[:setup]
61
63
  @skip = settings[:skip]
64
+ @tags = settings[:tags]
62
65
 
63
66
  @advice = @context ? @context.advice.dup : TestAdvice.new
64
67
 
65
68
  @tests = []
66
- @scope = scope_class.new(self)
69
+ @domain = domain_class.new(self)
67
70
 
68
71
  validate_settings
69
72
 
70
73
  evaluate(&block)
71
74
  end
72
75
 
76
+ #
73
77
  # Subclasses can override this methof to validate settings.
74
78
  # It is run just before evaluation of scope block.
79
+ #
75
80
  def validate_settings
76
81
  end
77
82
 
83
+ #
84
+ def domain
85
+ @domain
86
+ end
87
+
88
+ #
89
+ #
78
90
  #
79
91
  def evaluate(&block)
80
- @scope.module_eval(&block)
92
+ @domain.module_eval(&block)
81
93
  end
82
94
 
95
+ #
83
96
  # Iterate over each test and subcase.
97
+ #
84
98
  def each(&block)
85
99
  tests.each(&block)
86
100
  end
87
101
 
102
+ #
88
103
  # Number of tests and subcases.
104
+ #
89
105
  def size
90
106
  tests.size
91
107
  end
92
108
 
109
+ #
93
110
  # Subclasses of TestCase can override this to describe
94
111
  # the type of test case they define.
112
+ #
95
113
  def type
96
- 'Case'
114
+ 'Test Case'
97
115
  end
98
116
 
117
+ #
118
+ #
99
119
  #
100
120
  def to_s
101
121
  @label.to_s
102
122
  end
103
123
 
124
+ #
125
+ #
104
126
  #
105
127
  def skip?
106
128
  @skip
107
129
  end
108
130
 
131
+ #
132
+ #
109
133
  #
110
134
  def skip!(reason=true)
111
135
  @skip = reason
112
136
  end
113
137
 
138
+ #
139
+ #
140
+ #
141
+ def tags
142
+ @tags
143
+ end
144
+
145
+ #
114
146
  # Run test in the context of this case.
115
147
  #
116
148
  # @param [TestProc] test
@@ -132,48 +164,47 @@ module Lemon
132
164
  end
133
165
  end
134
166
 
167
+ #
135
168
  # Module for evaluating test case script.
136
169
  #
137
170
  # @return [Scope] evaluation scope
171
+ #
138
172
  def scope
139
- @scope
173
+ @scope ||= TestScope.new(self)
140
174
  end
141
175
 
142
- # Get the scope class dynamically so that each subclass
176
+ #
177
+ # Get the domain class dynamically so that each subclass
143
178
  # of TestCase will retrieve it's own.
144
- def scope_class
145
- self.class.const_get(:Scope)
179
+ #
180
+ def domain_class
181
+ self.class.const_get(:DSL)
146
182
  end
147
183
 
148
184
  #
149
- class Scope < World
185
+ class DSL < World
186
+
187
+ #
188
+ # The class for which this is a DSL context.
189
+ #
190
+ def context_class
191
+ TestCase
192
+ end
150
193
 
194
+ #
195
+ #
151
196
  #
152
197
  def initialize(testcase) #, &code)
153
198
  @_testcase = testcase
154
199
  @_setup = testcase.setup
155
200
  @_skip = nil
156
201
 
157
- extend testcase.context.scope if testcase.context
202
+ include testcase.context.domain if testcase.context
158
203
 
159
204
  #module_eval(&code)
160
205
  end
161
206
 
162
- #--
163
- # THINK: Instead of resuing TestCase can we have a TestContext
164
- # or other way to more generically mimics the parent context?
165
- #++
166
-
167
- ##
168
- #def context(label, &block)
169
- # @_testcase.tests << TestCase.new(
170
- # :testcase => @testcase,
171
- # :label => label,
172
- # &block
173
- # )
174
- #end
175
- #alias :Context :context
176
-
207
+ #
177
208
  # Setup is used to set things up for each unit test.
178
209
  # The setup procedure is run before each unit.
179
210
  #
@@ -187,10 +218,13 @@ module Lemon
187
218
  end
188
219
  alias :Setup :setup
189
220
 
221
+ #
190
222
  # Original Lemon nomenclature for `#setup`.
223
+ #
191
224
  alias :concern :setup
192
225
  alias :Concern :setup
193
226
 
227
+ #
194
228
  # Teardown procedure is used to clean-up after each unit test.
195
229
  #
196
230
  def teardown(&procedure)
@@ -198,11 +232,10 @@ module Lemon
198
232
  end
199
233
  alias :Teardown :teardown
200
234
 
201
- #--
202
235
  # TODO: Allow Before and After to handle setup and teardown?
203
236
  # But that would only allow one setup per case.
204
- #++
205
237
 
238
+ #
206
239
  # Define a _complex_ before procedure. The #before method allows
207
240
  # before procedures to be defined that are triggered by a match
208
241
  # against the unit's target method name or _aspect_ description.
@@ -233,6 +266,7 @@ module Lemon
233
266
  end
234
267
  alias :Before :before
235
268
 
269
+ #
236
270
  # Define a _complex_ after procedure. The #before method allows
237
271
  # before procedures to be defined that are triggered by a match
238
272
  # against the unit's target method name or _aspect_ description.
@@ -263,6 +297,81 @@ module Lemon
263
297
  end
264
298
  alias :After :after
265
299
 
300
+ # THINK: Instead of resuing TestCase can we have a TestContext
301
+ # or other way to more generically mimics the parent context?
302
+
303
+ #
304
+ # Create a subcase of module testcase.
305
+ #
306
+ def context(label, *tags, &block)
307
+ return if @_omit
308
+
309
+ @_testcase.tests << context_class.new(
310
+ :context => @_testcase,
311
+ :target => @_testcase.target,
312
+ :setup => @_setup,
313
+ :skip => @_skip,
314
+ :label => label,
315
+ :tags => tags,
316
+ &block
317
+ )
318
+ end
319
+ alias :Context :context
320
+
321
+ #
322
+ # Skip tests. Unlike omit, skipped tests are passed to the test harness,
323
+ # so they still can be included in reports, though they are not executed.
324
+ #
325
+ # If a block is given then only tests defined with-in the block are skipped.
326
+ # If no block is given then all subsquent tests in the test case are skipped.
327
+ #
328
+ # @param [String,Boolean] reason
329
+ # A description of the reason to skip the test, or simply a boolean value.
330
+ #
331
+ # @example
332
+ # skip "reason" do
333
+ # test do
334
+ # ...
335
+ # end
336
+ # end
337
+ #
338
+ def skip(reason=true)
339
+ if block_given?
340
+ @_skip = reason
341
+ yield
342
+ @_skip = nil
343
+ else
344
+ @_skip = reason
345
+ end
346
+ end
347
+ alias :Skip :skip
348
+
349
+ #
350
+ # Omitted tests are simply ignored and never instantiated let alone passed
351
+ # on to the test harness.
352
+ #
353
+ # If a block is given then only tests defined with-in the block are skipped.
354
+ # If no block is given then all subsquent tests in the test case are skipped.
355
+ #
356
+ #
357
+ # @example
358
+ # omit do
359
+ # test do
360
+ # ...
361
+ # end
362
+ # end
363
+ #
364
+ def omit(reason=true)
365
+ if block_given?
366
+ @_omit = reason
367
+ yield
368
+ @_omit = nil
369
+ else
370
+ @_omit = reason
371
+ end
372
+ end
373
+ alias :Omit :omit
374
+
266
375
  end
267
376
 
268
377
  end
@@ -9,18 +9,31 @@ module Lemon
9
9
 
10
10
  private
11
11
 
12
+ #
12
13
  # Make sure the target is a class.
13
- def validate_target
14
- raise unless Class === @target
14
+ #
15
+ def validate_settings
16
+ raise "#{@target} is not a module" unless Class === @target
15
17
  end
16
18
 
19
+ #
17
20
  # The type of testcase.
21
+ #
18
22
  def type
19
23
  'Class'
20
24
  end
21
25
 
22
26
  # Evaluation scope for {TestClass}.
23
- class Scope < TestModule::Scope
27
+ #
28
+ class DSL < TestModule::DSL
29
+
30
+ #
31
+ # The class for which this is a DSL context.
32
+ #
33
+ def context_class
34
+ TestClass
35
+ end
36
+
24
37
  end
25
38
 
26
39
  end
@@ -0,0 +1,58 @@
1
+ module Lemon
2
+
3
+ require 'lemon/test_method'
4
+
5
+ # Subclass of TestMethod used for class methods.
6
+ # It's basically the same class.
7
+ #
8
+ class TestClassMethod < TestMethod
9
+
10
+ # Description of the type of test case.
11
+ def type
12
+ 'Class Method'
13
+ end
14
+
15
+ # If class method, returns target method's name prefixed with double colons.
16
+ # If instance method, then returns target method's name prefixed with hash
17
+ # character.
18
+ def name
19
+ "::#{target}"
20
+ end
21
+
22
+ # Returns the prefixed method name.
23
+ def to_s
24
+ "::#{target}"
25
+ end
26
+
27
+ # Returns the fully qulaified name of the target method. This is
28
+ # the standard interface used by RubyTest.
29
+ def unit
30
+ "#{context}.#{target}"
31
+ end
32
+
33
+ # For a class method, the target class is the meta-class.
34
+ def target_class
35
+ @target_class ||= (class << context.target; self; end)
36
+ end
37
+
38
+ #
39
+ def class_method?
40
+ true
41
+ end
42
+
43
+ # Scope for evaluating class method test definitions.
44
+ #
45
+ class DSL < TestMethod::DSL
46
+
47
+ #
48
+ # The class for which this is a DSL context.
49
+ #
50
+ def context_class
51
+ TestClassMethod
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -8,64 +8,62 @@ module Lemon
8
8
  #
9
9
  class TestMethod < TestCase
10
10
 
11
- # New unit test.
12
11
  #
13
- # @option settings [Boolean] :function
14
- # Is the target method a class method, or not.
12
+ # New unit test.
15
13
  #
16
14
  def initialize(settings={}, &block)
17
15
  @tested = false
18
- @function = settings[:function]
19
16
  super(settings)
20
17
  end
21
18
 
19
+ #
22
20
  # Validate that a context and target method have been supplied.
21
+ #
23
22
  def validate_settings
24
23
  raise "method test has no module or class context" unless @context
25
24
  raise "#{@target} is not a method name" unless Symbol === @target
26
25
  end
27
26
 
28
- # Type is either `Method` or `Function` (a function is a class method).
27
+ #
28
+ # Description of the type of test case.
29
+ #
29
30
  def type
30
- if function?
31
- 'Function'
32
- else
33
- 'Method'
34
- end
31
+ 'Method'
35
32
  end
36
33
 
34
+ #
37
35
  # Used to make sure the the method has been tested, or not.
36
+ #
38
37
  attr_accessor :tested
39
38
 
40
- # Is this method a class method?
41
- def function?
42
- @function
43
- end
44
-
45
- # A function is also known as a "class method".
46
- alias :class_method? :function?
47
-
39
+ #
48
40
  # If class method, returns target method's name prefixed with double colons.
49
41
  # If instance method, then returns target method's name prefixed with hash
50
42
  # character.
43
+ #
51
44
  def name
52
- function? ? "::#{target}" : "##{target}"
45
+ "##{target}"
53
46
  end
54
47
 
55
48
  # TODO: If sub-cases are to be supported than we need to incorporate
56
49
  # the label into to_s.
57
50
 
51
+ #
58
52
  # Returns the prefixed method name.
53
+ #
59
54
  def to_s
60
- function? ? "::#{target}" : "##{target}"
55
+ "##{target}"
61
56
  end
62
57
 
58
+ #
63
59
  # Returns the fully qulaified name of the target method. This is
64
60
  # the standard interface used by Ruby Test.
61
+ #
65
62
  def unit
66
- function? ? "#{context}.#{target}" : "#{context}##{target}"
63
+ "#{context}##{target}"
67
64
  end
68
65
 
66
+ #
69
67
  # Run test in the context of this case. Notice that #run for
70
68
  # TestMethod is more complex than a general TestCase. This is
71
69
  # to ensure that the target method is invoked during the course
@@ -109,6 +107,8 @@ module Lemon
109
107
  raise_pending(test.procedure) unless test.tested
110
108
  end
111
109
 
110
+ #
111
+ #
112
112
  #
113
113
  def raise_pending(procedure)
114
114
  if RUBY_VERSION < '1.9'
@@ -118,22 +118,32 @@ module Lemon
118
118
  end
119
119
  end
120
120
 
121
- # If the target method is a class method, then the target class is the
122
- # meta-class, otherwise just the class itself.
121
+ #
122
+ # The target class.
123
+ #
123
124
  def target_class
124
- @target_class ||= (
125
- if function?
126
- (class << context.target; self; end)
127
- else
128
- context.target
129
- end
130
- )
125
+ @target_class ||= context.target
126
+ end
127
+
128
+ #
129
+ #
130
+ #
131
+ def class_method?
132
+ false
131
133
  end
132
134
 
133
135
  # Scope for evaluating method test definitions.
134
136
  #
135
- class Scope < TestCase::Scope
137
+ class DSL < TestCase::DSL
136
138
 
139
+ #
140
+ # The class for which this is a DSL context.
141
+ #
142
+ def context_class
143
+ TestMethod
144
+ end
145
+
146
+ #
137
147
  # Define a unit test for this case.
138
148
  #
139
149
  # @example
@@ -141,64 +151,43 @@ module Lemon
141
151
  # puts "Hello"
142
152
  # end
143
153
  #
144
- def test(label=nil, &block)
145
- block = Omission.new(@_omit).to_proc if @_omit
154
+ def test(label=nil, *tags, &block)
155
+ return if @_omit
156
+
146
157
  test = TestProc.new(
147
158
  :context => @_testcase,
148
159
  :setup => @_setup,
149
160
  :skip => @_skip,
150
161
  :label => label,
162
+ :tags => tags,
151
163
  &block
152
164
  )
165
+
153
166
  @_testcase.tests << test
167
+
154
168
  test
155
169
  end
156
- alias :Test :test
157
170
 
158
- # Create a sub-case ofr the method case.
159
- def context(label, &block)
171
+ #
172
+ # Create a sub-case of the method case.
173
+ #
174
+ def context(label, *tags, &block)
175
+ return if @_omit
176
+
160
177
  @_testcase.tests << TestMethod.new(
161
178
  :context => @_testcase,
162
179
  :target => @_testcase.target,
163
180
  :setup => @_setup,
164
181
  :skip => @_skip,
165
182
  :label => label,
183
+ :tags => tags,
166
184
  &block
167
185
  )
168
186
  end
169
- alias :Context :context
170
-
171
- # Omit tests.
172
- #
173
- # @example
174
- # omit "reason" do
175
- # test do
176
- # ...
177
- # end
178
- # end
179
- #
180
- def omit(label=true, &block)
181
- @_omit = label
182
- block.call
183
- @_omit = nil
184
- end
185
- alias :Omit :omit
186
187
 
187
- # Skip tests. Unlike omit, skipped tests are not executed at all.
188
- #
189
- # @example
190
- # skip "reason" do
191
- # test do
192
- # ...
193
- # end
194
- # end
195
- #
196
- def skip(label=true, &block)
197
- @_skip = label
198
- block.call
199
- @_skip = nil
200
- end
201
- alias :Skip :skip
188
+ # Capitialized term.
189
+ alias :Test :test
190
+ alias :Context :context
202
191
 
203
192
  end
204
193