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
data/HISTORY.rdoc CHANGED
@@ -1,5 +1,20 @@
1
1
  = RELEASE HISTORY
2
2
 
3
+ == 0.8.2 / 2010-09-05
4
+
5
+ This release overhauls how coverage is performed so it does not need to
6
+ take a system snapshot after requiring each covered file. This greatly
7
+ improves Lemon's speed. In addition #setup and #teardown have been introduced
8
+ for performing procedures before and after each unit test.
9
+
10
+ Changes:
11
+
12
+ * Overhaul coverage analysis.
13
+ * Add TestCase#setup and #teardown methods.
14
+ * TestCase#concern and #context are just aliases of #setup.
15
+ * Improved output formats.
16
+
17
+
3
18
  == 0.8.1 / 2010-07-11
4
19
 
5
20
  This release adds a timer to the verbose output, which help isolate unit tests
data/README.rdoc CHANGED
@@ -23,30 +23,40 @@ Say our library 'mylib.rb' consists of the class X:
23
23
 
24
24
  The simplest test case would be written as follows:
25
25
 
26
- Covers 'mylib'
26
+ covers 'mylib'
27
27
 
28
- TestCase X do
29
- Unit :a => "method #a does something expected" do
28
+ testcase X do
29
+ unit :a => "method #a does something expected" do
30
30
  x = X.new
31
31
  x.a.assert.is_a? String
32
32
  end
33
33
  end
34
34
 
35
- The +Covers+ method works just like +require+ with the exception that loading the file does not occur until just before the tests are run. This allows Lemon to calculate accurate coverage reports.
35
+ The +Covers+ method works just like +require+ with the exception that Lemon records the file for refernce --under certain scenarios it can be used to improve overall test covered.
36
36
 
37
- As tests grow, we might need to organize them into special concerns. For this Lemon provides the #Concern method.
37
+ As tests grow, we might need to organize them into special concerns. For this Lemon provides a #concern method and a #setup method. Technically the two methods are the same, but #concern is used more for descriptive purposes whereas #setup is used to create an instance of the testcase's target class.
38
38
 
39
- Covers 'mylib'
39
+ covers 'mylib'
40
40
 
41
- TestCase X do
42
- Concern "Description of a concern that the following unit tests address."
41
+ testcase X do
42
+ setup "Description of a concern that the following unit tests address." do
43
+ X.new
44
+ end
43
45
 
44
- Unit :a => "method #a does something expected" do
45
- x = X.new
46
+ unit :a => "method #a does something expected" do |x|
46
47
  x.a.assert.is_a? String
47
48
  end
48
49
  end
49
50
 
51
+ Notice that the parameter passed to the block of +unit+ methdod is the instance of +X+ created in the +setup+ block. This block is run for every subsequent +Unit+ untill a new concern is defined.
52
+
53
+ In conjunction with the #setup methods, there is a #teardown method which can be used "tidy-up" after each unit run if need be.
54
+
55
+ Lastly, there are the +before+ and +after+ methods which can be used only once for each testcase. The +before+ method defines a procedure to run before any of the testcase's units are run, and the +after+ method defines a procedure to run after that are all finished.
56
+
57
+ That is the bulk of the matter forf writing Lemon tests. There are few other features not mentions here. You can learn more about hose by reading the wiki[http://wiki.github.com/proutils/lemon].
58
+
59
+
50
60
  === Running Tests
51
61
 
52
62
  To run tests use the +lemon+ command-line utility.
@@ -57,26 +67,34 @@ Normal output is typical <i>dot-progress</i>. For verbose output, use the <code>
57
67
 
58
68
  $ lemon -v test/cases/name_case.rb
59
69
 
70
+ Other output types can be specified by the `--format` or `-f` option.
71
+
72
+ $ lemon -f tap test/cases/name_case.rb
73
+
74
+
60
75
  === Checking Test Coverage
61
76
 
62
77
  Lemon can check test coverage by loading your target system and comparing it to your tests. To do this supply the <code>lemon</code> command the <code>--coverage</code> or <code>-c</code> option.
63
78
 
64
- $ lemon --coverage -Ilib test/cases/
79
+ $ lemon -c -Ilib test/cases/*.rb
65
80
 
66
81
  === Generating Test Skeletons
67
82
 
68
83
  Because of the one to one correspondence of case-unit to class-method, Lemon can also generate test scaffolding for previously written code. To do this, use the <code>--generate</code> or <code>-g</code> option and provide the lib location, or files, of the scripts for which to generate test scaffolding, and the output location for the test scripts.
69
84
 
70
- $ lemon --generate -Ilib test/cases/
85
+ $ lemon -g -Ilib test/cases/*.rb
71
86
 
72
87
  Generating test case scaffolding from code will undoubtedly strike test-driven developers as a case of putting the cart before the horse. However, it is not unreasonable to argue that high-level, behavior-driven, functional testing frameworks, such as Q.E.D. and Cucumber are better suited to test-first methodologies. While test-driven development can obviously be done with Lemon, unit-testing is more appropriate for testing specific, critical portions of code, or for achieving full test coverage for mission critical applications.
73
88
 
89
+ === Test Directory
90
+
91
+ There is no special directory for Lemon tests. Since they are unit tests, `test/` or `test/unit/` are good choices. Other options are `cases/` and `test/cases` since each file generally defines a single testcase. However, I recommend using interface-based names, regardless of the framework you actually use. In which case, `test/unit` is still a good choice, but also `test/api` if Lemon will be the only framework you use to test the API directly.
92
+
74
93
 
75
94
  == COPYRIGHT
76
95
 
77
- (MIT License)
96
+ (Apache 2.0 License)
78
97
 
79
98
  Copyright (c) 2009 Thomas Sawyer
80
99
 
81
100
  See LICENSE file for details.
82
-
data/bin/lemon CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'lemon/command'
3
- Lemon::Command.run
2
+ require 'lemon/cli'
3
+ Lemon::CLI.run(ARGV)
4
+
@@ -0,0 +1,15 @@
1
+ covers File.dirname(__FILE__) + '/fixture/example.rb'
2
+
3
+ testcase Example do
4
+
5
+ unit :f => "one and one is two"do
6
+ Example.new.f(1,1).assert == 2
7
+ end
8
+
9
+ unit :f do
10
+ ex = Example.new
11
+ ex.f(1,2).assert == 4
12
+ end
13
+
14
+ end
15
+
@@ -0,0 +1,32 @@
1
+ covers File.dirname(__FILE__) + '/fixture/example.rb'
2
+
3
+ testcase Example do
4
+
5
+ setup "without multipler" do
6
+ Example.new
7
+ end
8
+
9
+ unit :f do |ex|
10
+ ex.f(1,2).assert == 3
11
+ ex.f(2,2).assert == 4
12
+ end
13
+
14
+ setup "with multipler" do
15
+ Example.new(2)
16
+ end
17
+
18
+ unit :f => "incorporate the multiplier" do |ex|
19
+ ex.f(1,2).assert == 4
20
+ ex.f(2,2).assert == 6
21
+ end
22
+
23
+ teardown do
24
+ # ...
25
+ end
26
+
27
+ meta :m do
28
+ Example.m(1,1).assert == 1
29
+ end
30
+
31
+ end
32
+
@@ -0,0 +1,14 @@
1
+ covers File.dirname(__FILE__) + '/fixture/example.rb'
2
+
3
+ testcase Example do
4
+
5
+ unit :f => "one and one is two"do
6
+ Example.new.f(1,1).assert == 2
7
+ end
8
+
9
+ unit :f do
10
+ raise Pending
11
+ end
12
+
13
+ end
14
+
@@ -0,0 +1,10 @@
1
+ covers File.dirname(__FILE__) + '/fixture/example.rb'
2
+
3
+ testcase Example do
4
+
5
+ unit :f do
6
+ # notice Example#f has not been called
7
+ end
8
+
9
+ end
10
+
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/example.rb'
2
+
3
+ ex = Example.new
4
+ ex.f(1,2)
5
+
@@ -0,0 +1,20 @@
1
+ class Example
2
+
3
+ def initialize(a=1)
4
+ @a = a
5
+ end
6
+
7
+ def f(x,y)
8
+ @a * x + y
9
+ end
10
+
11
+ def q(x,y)
12
+ x % y
13
+ end
14
+
15
+ def self.m(a,b)
16
+ a * b
17
+ end
18
+
19
+ end
20
+
data/lib/lemon.rb CHANGED
@@ -1,2 +1,2 @@
1
- require 'lemon/runner'
2
- require 'lemon/coverage'
1
+ require 'lemon/meta/data'
2
+ require 'lemon/controller/coverage_analyzer'
data/lib/lemon/cli.rb ADDED
@@ -0,0 +1,281 @@
1
+ module Lemon
2
+
3
+ require 'lemon/meta/data'
4
+ require 'optparse'
5
+
6
+ # TODO: What about a config file?
7
+
8
+ # CLI Interface handle all lemon sub-commands.
9
+ class CLI
10
+
11
+ COMMANDS = {
12
+ '-t' => 'test' , '--test' => 'test',
13
+ '-c' => 'coverage', '--cov' => 'coverage', '--coverage' => 'coverage',
14
+ '-g' => 'generate', '--gen' => 'generate', '--generate' => 'generate'
15
+ }
16
+
17
+ #
18
+ def self.run(argv=ARGV)
19
+ new.run(argv)
20
+ end
21
+
22
+ #
23
+ def initialize(argv=ARGV)
24
+ @options = {}
25
+ end
26
+
27
+ #
28
+ def options
29
+ @options
30
+ end
31
+
32
+ #
33
+ def run(argv)
34
+ cmdopt = COMMANDS.keys.find{ |k| argv.delete(k) }
35
+ command = COMMANDS[cmdopt]
36
+
37
+ if command.nil? && argv.include?('--help')
38
+ option_commands
39
+ option_parser.parse!(argv)
40
+ end
41
+
42
+ command ||= 'test'
43
+
44
+ #option_commands
45
+
46
+ #cmd = argv.shift
47
+ #cmd = COMMANDS.find{ |c| /^#{cmd}/ =~ c }
48
+ #option_parser.order!(argv)
49
+
50
+ begin
51
+ __send__("#{command}_parse", argv)
52
+ __send__("#{command}", argv)
53
+ rescue => err
54
+ raise err if $DEBUG
55
+ $stderr.puts('ERROR: ' + err.to_s)
56
+ end
57
+ end
58
+
59
+ # T E S T
60
+
61
+ # Run unit tests.
62
+ def test(scripts)
63
+ require 'lemon/controller/test_runner'
64
+
65
+ loadpath = options[:loadpath] || []
66
+ requires = options[:requires] || []
67
+
68
+ loadpath.each{ |path| $LOAD_PATH.unshift(path) }
69
+ requires.each{ |path| require(path) }
70
+
71
+ #suite = Lemon::Test::Suite.new(files, :cover=>cover)
72
+ #runner = Lemon::Runner.new(suite, :format=>format, :cover=>cover, :namespaces=>namespaces)
73
+
74
+ runner = Lemon::TestRunner.new(
75
+ scripts, :format=>options[:format], :namespaces=>options[:namespaces]
76
+ )
77
+
78
+ runner.run
79
+ end
80
+
81
+ #
82
+ def test_parse(argv)
83
+ option_parser.banner = "Usage: lemon [-t] [options] [files ...]"
84
+ #option_parser.separator("Run unit tests.")
85
+
86
+ option_format
87
+ option_verbose
88
+ option_namespaces
89
+ option_loadpath
90
+ option_requires
91
+
92
+ option_parser.parse!(argv)
93
+ end
94
+
95
+ # C O V E R A G E
96
+
97
+ # Ouput coverage report.
98
+ def coverage(test_files)
99
+ require 'lemon/controller/coverage_analyzer'
100
+
101
+ #loadpath = options[:loadpath] || []
102
+ #requires = options[:requires] || []
103
+
104
+ #loadpath.each{ |path| $LOAD_PATH.unshift(path) }
105
+ #requires.each{ |path| require(path) }
106
+
107
+ $stderr.print "Calculating... "
108
+ $stderr.flush
109
+
110
+ cover = Lemon::CoverageAnalyzer.new(test_files, options)
111
+
112
+ cover.calculate # this just helps calcs get done up front
113
+
114
+ $stderr.puts
115
+
116
+ cover.render
117
+ end
118
+
119
+ #
120
+ def coverage_parse(argv)
121
+ option_parser.banner = "Usage: lemon -c [options] [files ...]"
122
+ #option_parser.separator("Check test coverage.")
123
+
124
+ option_namespaces
125
+ option_private
126
+ option_zealous
127
+ option_output
128
+ option_format
129
+ option_loadpath
130
+ option_requires
131
+
132
+ option_parser.parse!(argv)
133
+ end
134
+
135
+ # G E N E R A T E
136
+
137
+ # Generate test templates.
138
+ def generate(test_files)
139
+ require 'lemon/controller/scaffold_generator'
140
+
141
+ loadpath = options[:loadpath] || []
142
+ requires = options[:requires] || []
143
+
144
+ loadpath.each{ |path| $LOAD_PATH.unshift(path) }
145
+ requires.each{ |path| require(path) }
146
+
147
+ #cover = options[:uncovered]
148
+ #suite = Lemon::TestSuite.new(test_files, :cover=>cover) #, :cover_all=>true)
149
+ generator = Lemon::ScaffoldGenerator.new(test_files, options)
150
+
151
+ #if uncovered
152
+ # puts cover.generate_uncovered #(output)
153
+ #else
154
+ puts generator.generate #(output)
155
+ #end
156
+ end
157
+
158
+ #
159
+ def generate_parse(argv)
160
+ option_parser.banner = "Usage: lemon -g [options] [files ...]"
161
+ #option_parser.separator("Generate test scaffolding.")
162
+
163
+ option_namespaces
164
+ option_uncovered
165
+ option_all
166
+ option_private
167
+ option_loadpath
168
+ option_requires
169
+
170
+ option_parser.parse!(argv)
171
+ end
172
+
173
+ # P A R S E R O P T I O N S
174
+
175
+ def option_commands
176
+ option_parser.banner = "Usage: lemon [command] [options] [files ...]"
177
+ option_parser.on('-t', '--test', 'run unit tests [default]') do
178
+ @command = :test
179
+ end
180
+ option_parser.on('-c', '--cov', '--coverage', 'provide test coverage analysis') do
181
+ @command = :coverage
182
+ end
183
+ option_parser.on('-g', '--gen', '--generate', 'generate unit test scaffolding') do
184
+ @command = :generate
185
+ end
186
+ end
187
+
188
+ def option_namespaces
189
+ option_parser.on('-n', '--namespace NAME', 'add a namespace to output') do |name|
190
+ options[:namespaces] ||= []
191
+ options[:namespaces] << name
192
+ end
193
+ end
194
+
195
+ #def option_framework
196
+ # option_parser.on('-f', '--framework NAME', 'framework syntax to output') do |name|
197
+ # options[:framework] = name.to_sym
198
+ # end
199
+ #end
200
+
201
+ def option_format
202
+ option_parser.on('-f', '--format NAME', 'output format') do |name|
203
+ options[:format] = name
204
+ end
205
+ end
206
+
207
+ def option_verbose
208
+ option_parser.on('-v', '--verbose', 'shortcut for `-f verbose`') do |name|
209
+ options[:format] = 'verbose'
210
+ end
211
+ end
212
+
213
+ def option_uncovered
214
+ option_parser.on('-u', '--uncovered', 'include only uncovered units') do
215
+ options[:uncovered] = true
216
+ end
217
+ end
218
+
219
+ def option_all
220
+ option_parser.on('-a', '--all', 'include all namespaces and units') do
221
+ options[:all] = true
222
+ end
223
+ end
224
+
225
+ def option_private
226
+ option_parser.on('-p', '--private', 'include private and protected methods') do
227
+ options[:private] = true
228
+ end
229
+ end
230
+
231
+ def option_zealous
232
+ option_parser.on('-z', '--zealous', 'include undefined case methods') do
233
+ options[:zealous] = true
234
+ end
235
+ end
236
+
237
+ def option_output
238
+ option_parser.on('-o', '--output DIRECTORY', 'log directory') do |dir|
239
+ options[:output] = dir
240
+ end
241
+ end
242
+
243
+ def option_loadpath
244
+ option_parser.on("-I PATH" , 'add directory to $LOAD_PATH') do |path|
245
+ paths = path.split(/[:;]/)
246
+ options[:loadpath] ||= []
247
+ options[:loadpath].concat(paths)
248
+ end
249
+ end
250
+
251
+ def option_requires
252
+ option_parser.on("-r FILE" , 'require file(s) (before doing anything else)') do |files|
253
+ files = files.split(/[:;]/)
254
+ options[:requires] ||= []
255
+ options[:requires].concat(files)
256
+ end
257
+ end
258
+
259
+ def option_parser
260
+ @option_parser ||= (
261
+ OptionParser.new do |opt|
262
+ opt.on_tail("--debug" , 'turn on debugging mode') do
263
+ $DEBUG = true
264
+ end
265
+ opt.on_tail("--about" , 'display information about lemon') do
266
+ puts "Lemon v#{Lemon::VERSION}"
267
+ puts "#{Lemon::COPYRIGHT}"
268
+ exit
269
+ end
270
+ opt.on_tail('-h', '--help', 'display help (also try `<command> --help`)') do
271
+ puts opt
272
+ exit
273
+ end
274
+ end
275
+ )
276
+ end
277
+
278
+ end
279
+
280
+ end
281
+