lemon 0.5 → 0.6
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.
- data/MANIFEST +6 -0
- data/lib/lemon/command.rb +27 -107
- data/lib/lemon/commands/coverage.rb +103 -0
- data/lib/lemon/commands/generate.rb +108 -0
- data/lib/lemon/commands/test.rb +88 -0
- data/lib/lemon/coverage.rb +45 -26
- data/lib/lemon/dsl.rb +59 -0
- data/lib/lemon/reporter.rb +19 -25
- data/lib/lemon/reporters/dotprogress.rb +30 -22
- data/lib/lemon/reporters/verbose.rb +50 -17
- data/lib/lemon/runner.rb +11 -4
- data/lib/lemon/test/case.rb +8 -0
- data/lib/lemon/test/concern.rb +1 -1
- data/lib/lemon/test/suite.rb +18 -30
- data/lib/lemon/test/unit.rb +2 -2
- data/meta/version +1 -1
- data/test/cases/testcase_case.rb +1 -1
- metadata +8 -3
data/MANIFEST
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
COPYING
|
2
2
|
COPYING.LESSER
|
3
3
|
HISTORY
|
4
|
+
MANIFEST
|
4
5
|
README.rdoc
|
5
6
|
bin/lemon
|
6
7
|
lib/lemon
|
7
8
|
lib/lemon.rb
|
8
9
|
lib/lemon/command.rb
|
10
|
+
lib/lemon/commands
|
11
|
+
lib/lemon/commands/coverage.rb
|
12
|
+
lib/lemon/commands/generate.rb
|
13
|
+
lib/lemon/commands/test.rb
|
9
14
|
lib/lemon/coverage.rb
|
15
|
+
lib/lemon/dsl.rb
|
10
16
|
lib/lemon/kernel.rb
|
11
17
|
lib/lemon/reporter.rb
|
12
18
|
lib/lemon/reporters
|
data/lib/lemon/command.rb
CHANGED
@@ -1,125 +1,45 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'lemon'
|
4
|
-
require 'lemon/coverage'
|
5
1
|
require 'optparse'
|
6
|
-
|
2
|
+
|
3
|
+
#require 'lemon'
|
4
|
+
#require 'yaml'
|
7
5
|
|
8
6
|
module Lemon
|
9
7
|
|
10
|
-
# Lemon Command-line tool.
|
8
|
+
# Lemon Command-line tool base class.
|
11
9
|
class Command
|
12
10
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
attr_accessor :command
|
19
|
-
attr_accessor :format
|
20
|
-
attr_accessor :requires
|
21
|
-
attr_accessor :includes
|
22
|
-
attr_accessor :public_only
|
23
|
-
|
24
|
-
# New Command instance.
|
25
|
-
def initialize
|
26
|
-
@format = nil
|
27
|
-
@requires = []
|
28
|
-
@includes = []
|
29
|
-
@public_only = false
|
30
|
-
end
|
31
|
-
|
32
|
-
# Instance of OptionParser.
|
33
|
-
def parser
|
34
|
-
@parser ||= OptionParser.new do |opt|
|
35
|
-
opt.on('--verbose', '-v', "select verbose report format") do |type|
|
36
|
-
self.format = :verbose
|
37
|
-
end
|
38
|
-
#opt.on('--format', '-f [TYPE]', "select alternate report format") do |type|
|
39
|
-
# self.format = type
|
40
|
-
#end
|
41
|
-
opt.on('--coverage', '-c', 'produce a coverage report') do
|
42
|
-
self.command = :coverage
|
43
|
-
end
|
44
|
-
opt.on('--generate', '-g', 'generate test skeletons') do
|
45
|
-
self.command = :generate
|
46
|
-
end
|
47
|
-
opt.on('--public', '-p', "only include public methods (for -c and -g)") do
|
48
|
-
self.public_only = true
|
49
|
-
end
|
50
|
-
opt.on("-I [PATH]" , 'include in $LOAD_PATH') do |path|
|
51
|
-
self.includes = path
|
52
|
-
end
|
53
|
-
opt.on("-r [FILES]" , 'library files to require') do |files|
|
54
|
-
self.requires = files
|
55
|
-
end
|
56
|
-
opt.on("--debug" , 'turn on debugging mode') do
|
57
|
-
$DEBUG = true
|
58
|
-
end
|
59
|
-
opt.on_tail('--help', '-h', 'show this help message') do
|
60
|
-
puts opt
|
61
|
-
exit
|
62
|
-
end
|
63
|
-
end
|
11
|
+
# Used to map command-line options to command classes.
|
12
|
+
# This must be overridden in subclasses, and return an
|
13
|
+
# array of of options, e.g. [ '-g', '--generate'].
|
14
|
+
def self.options
|
15
|
+
raise "not implemented"
|
64
16
|
end
|
65
17
|
|
66
|
-
#
|
67
|
-
def
|
68
|
-
@
|
18
|
+
# Stores a list of command classes.
|
19
|
+
def self.commands
|
20
|
+
@commands ||= []
|
69
21
|
end
|
70
22
|
|
71
|
-
#
|
72
|
-
def
|
73
|
-
|
23
|
+
# When this class is inherited, it is registered to the commands list.
|
24
|
+
def self.inherited(command_class)
|
25
|
+
commands << command_class
|
74
26
|
end
|
75
27
|
|
76
|
-
#
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
def run
|
83
|
-
parser.parse!
|
84
|
-
|
85
|
-
includes.each do |path|
|
86
|
-
$LOAD_PATHS.unshift(path)
|
87
|
-
end
|
88
|
-
|
89
|
-
tests = ARGV
|
90
|
-
|
91
|
-
case command
|
92
|
-
when :coverage
|
93
|
-
coverage(tests)
|
94
|
-
when :generate
|
95
|
-
generate(tests)
|
96
|
-
else
|
97
|
-
test(tests)
|
28
|
+
# Factory method to initialize and run choosen sub-command.
|
29
|
+
def self.run
|
30
|
+
cmd = commands.find do |command_class|
|
31
|
+
[command_class.options].flatten.find do |opt|
|
32
|
+
ARGV.delete(opt)
|
33
|
+
end
|
98
34
|
end
|
99
|
-
|
100
|
-
|
101
|
-
# Check test coverage.
|
102
|
-
def coverage(tests)
|
103
|
-
cover = Lemon::Coverage.new(requires, :public => public_only?)
|
104
|
-
suite = Lemon::Test::Suite.new(tests)
|
105
|
-
puts cover.coverage(suite).to_yaml
|
106
|
-
end
|
107
|
-
|
108
|
-
# Generate test skeletons.
|
109
|
-
def generate(tests)
|
110
|
-
cover = Lemon::Coverage.new(requires, :public => public_only?)
|
111
|
-
#suite = Lemon::Test::Suite.new(tests)
|
112
|
-
puts cover.generate #(suite).to_yaml
|
113
|
-
end
|
114
|
-
|
115
|
-
# Run unit tests.
|
116
|
-
def test(tests)
|
117
|
-
requires.each{ |path| require(path) }
|
118
|
-
suite = Lemon::Test::Suite.new(tests)
|
119
|
-
runner = Lemon::Runner.new(suite, format)
|
120
|
-
runner.run
|
35
|
+
cmd ? cmd.run : Commands::Test.run
|
121
36
|
end
|
122
37
|
|
123
38
|
end
|
124
39
|
|
125
40
|
end
|
41
|
+
|
42
|
+
require 'lemon/commands/test'
|
43
|
+
require 'lemon/commands/coverage'
|
44
|
+
require 'lemon/commands/generate'
|
45
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Lemon
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
# Lemon Coverage Command-line tool.
|
5
|
+
class Coverage < Command
|
6
|
+
require 'yaml'
|
7
|
+
require 'lemon/coverage'
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
['-c', '--coverage']
|
11
|
+
end
|
12
|
+
|
13
|
+
# Initialize and run.
|
14
|
+
def self.run
|
15
|
+
new.run
|
16
|
+
end
|
17
|
+
|
18
|
+
# New Command instance.
|
19
|
+
def initialize
|
20
|
+
@requires = []
|
21
|
+
@includes = []
|
22
|
+
@namespaces = []
|
23
|
+
@public_only = false
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
attr_accessor :public_only
|
28
|
+
|
29
|
+
#
|
30
|
+
def public_only?
|
31
|
+
@public_only
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get or set librarires to pre-require.
|
35
|
+
def requires(*paths)
|
36
|
+
@requires.concat(paths) unless paths.empty?
|
37
|
+
@requires
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get or set paths to include in $LOAD_PATH.
|
41
|
+
def includes(*paths)
|
42
|
+
@includes.concat(paths) unless paths.empty?
|
43
|
+
@includes
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get or set paths to include in $LOAD_PATH.
|
47
|
+
def namespaces(*names)
|
48
|
+
@namespaces.concat(names) unless names.empty?
|
49
|
+
@namespaces
|
50
|
+
end
|
51
|
+
|
52
|
+
# Instance of OptionParser.
|
53
|
+
def parser
|
54
|
+
@parser ||= OptionParser.new do |opt|
|
55
|
+
opt.banner = "lemon -c [OPTIONS]"
|
56
|
+
opt.separator("Produce test coverage reports.")
|
57
|
+
opt.on('--namespace', '-n [NAME]', "include namespace") do |name|
|
58
|
+
namespaces(name)
|
59
|
+
end
|
60
|
+
opt.on('--public', '-p', "only include public methods") do
|
61
|
+
self.public_only = true
|
62
|
+
end
|
63
|
+
opt.on("-r [FILES]" , 'library files to require') do |files|
|
64
|
+
files = files.split(/[:;]/)
|
65
|
+
requires(*files)
|
66
|
+
end
|
67
|
+
opt.on("-I [PATH]" , 'include in $LOAD_PATH') do |path|
|
68
|
+
path = path.split(/[:;]/)
|
69
|
+
includes(*path)
|
70
|
+
end
|
71
|
+
opt.on("--debug" , 'turn on debugging mode') do
|
72
|
+
$DEBUG = true
|
73
|
+
end
|
74
|
+
opt.on_tail('--help', '-h', 'show this help message') do
|
75
|
+
puts opt
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
def run
|
83
|
+
parser.parse!
|
84
|
+
|
85
|
+
test_files = ARGV.dup
|
86
|
+
load_files = []
|
87
|
+
|
88
|
+
includes.each do |path|
|
89
|
+
$LOAD_PATHS.unshift(path)
|
90
|
+
end
|
91
|
+
|
92
|
+
requires.each{ |path| require(path) }
|
93
|
+
|
94
|
+
cover = Lemon::Coverage.new(load_files, namespaces, :public => public_only?)
|
95
|
+
suite = Lemon::Test::Suite.new(test_files)
|
96
|
+
puts cover.coverage(suite).to_yaml
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Lemon
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
# Lemon Generate Command-line tool.
|
5
|
+
class Generate < Command
|
6
|
+
require 'lemon/coverage'
|
7
|
+
|
8
|
+
#
|
9
|
+
def self.options
|
10
|
+
['-g', '--generate']
|
11
|
+
end
|
12
|
+
|
13
|
+
# Initialize and run.
|
14
|
+
def self.run
|
15
|
+
new.run
|
16
|
+
end
|
17
|
+
|
18
|
+
# New Command instance.
|
19
|
+
def initialize
|
20
|
+
@requires = []
|
21
|
+
@includes = []
|
22
|
+
@namespaces = []
|
23
|
+
@public_only = false
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
attr_accessor :output
|
28
|
+
|
29
|
+
#
|
30
|
+
attr_accessor :public_only
|
31
|
+
|
32
|
+
#
|
33
|
+
def public_only?
|
34
|
+
@public_only
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get or set librarires to pre-require.
|
38
|
+
def requires(*paths)
|
39
|
+
@requires.concat(paths) unless paths.empty?
|
40
|
+
@requires
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get or set paths to include in $LOAD_PATH.
|
44
|
+
def includes(*paths)
|
45
|
+
@includes.concat(paths) unless paths.empty?
|
46
|
+
@includes
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get or set paths to include in $LOAD_PATH.
|
50
|
+
def namespaces(*names)
|
51
|
+
@namespaces.concat(names) unless names.empty?
|
52
|
+
@namespaces
|
53
|
+
end
|
54
|
+
|
55
|
+
# Instance of OptionParser.
|
56
|
+
def parser
|
57
|
+
@parser ||= OptionParser.new do |opt|
|
58
|
+
opt.banner = "lemon -g [OPTIONS]"
|
59
|
+
opt.separator("Generate test scaffolding.")
|
60
|
+
opt.on("--namespace", "-n [NAME]", "include namespace") do |name|
|
61
|
+
namespaces(name)
|
62
|
+
end
|
63
|
+
opt.on("--public", "-p", "only include public methods") do
|
64
|
+
self.public_only = true
|
65
|
+
end
|
66
|
+
opt.on("--output", "-o [PATH]", "output directory") do |path|
|
67
|
+
self.output = path
|
68
|
+
end
|
69
|
+
opt.on("-r [FILES]" , "library files to require") do |files|
|
70
|
+
files = files.split(/[:;]/)
|
71
|
+
requires(*files)
|
72
|
+
end
|
73
|
+
opt.on("-I [PATH]" , "include in $LOAD_PATH") do |path|
|
74
|
+
path = path.split(/[:;]/)
|
75
|
+
includes(*path)
|
76
|
+
end
|
77
|
+
opt.on("--debug" , "turn on debugging mode") do
|
78
|
+
$DEBUG = true
|
79
|
+
end
|
80
|
+
opt.on_tail("--help", "-h", "show this help message") do
|
81
|
+
puts opt
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Generate test skeletons.
|
88
|
+
def run
|
89
|
+
parser.parse!
|
90
|
+
|
91
|
+
test_files = ARGV.dup
|
92
|
+
|
93
|
+
includes.each do |path|
|
94
|
+
$LOAD_PATHS.unshift(path)
|
95
|
+
end
|
96
|
+
|
97
|
+
requires.each{ |path| require(path) }
|
98
|
+
|
99
|
+
cover = Lemon::Coverage.new([], namespaces, :public=>public_only?)
|
100
|
+
suite = Lemon::Test::Suite.new(*test_files)
|
101
|
+
puts cover.generate(output) #(suite).to_yaml
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Lemon
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
# Lemon Test Command-line tool.
|
5
|
+
class Test < Command
|
6
|
+
require 'lemon/runner'
|
7
|
+
|
8
|
+
def self.options
|
9
|
+
[]
|
10
|
+
end
|
11
|
+
|
12
|
+
# Initialize and run.
|
13
|
+
def self.run
|
14
|
+
new.run
|
15
|
+
end
|
16
|
+
|
17
|
+
# New Command instance.
|
18
|
+
def initialize
|
19
|
+
@format = nil
|
20
|
+
@requires = []
|
21
|
+
@includes = []
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
attr_accessor :format
|
26
|
+
|
27
|
+
# Get or set librarires to pre-require.
|
28
|
+
def requires(*paths)
|
29
|
+
@requires.concat(paths) unless paths.empty?
|
30
|
+
@requires
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get or set paths to include in $LOAD_PATH.
|
34
|
+
def includes(*paths)
|
35
|
+
@includes.concat(paths) unless paths.empty?
|
36
|
+
@includes
|
37
|
+
end
|
38
|
+
|
39
|
+
# Instance of OptionParser.
|
40
|
+
def parser
|
41
|
+
@parser ||= OptionParser.new do |opt|
|
42
|
+
opt.separator("Run unit tests.")
|
43
|
+
opt.on('--verbose', '-v', "select verbose report format") do |type|
|
44
|
+
self.format = :verbose
|
45
|
+
end
|
46
|
+
#opt.on('--format', '-f [TYPE]', "select alternate report format") do |type|
|
47
|
+
# self.format = type
|
48
|
+
#end
|
49
|
+
opt.on("-r [FILES]" , 'library files to require') do |files|
|
50
|
+
files = files.split(/[:;]/)
|
51
|
+
requires(*files)
|
52
|
+
end
|
53
|
+
opt.on("-I [PATH]" , 'include in $LOAD_PATH') do |path|
|
54
|
+
paths = path.split(/[:;]/)
|
55
|
+
includes(*paths)
|
56
|
+
end
|
57
|
+
opt.on("--debug" , 'turn on debugging mode') do
|
58
|
+
$DEBUG = true
|
59
|
+
end
|
60
|
+
opt.on_tail('--help', '-h', 'show this help message') do
|
61
|
+
puts opt
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Run unit tests.
|
68
|
+
def run
|
69
|
+
parser.parse!
|
70
|
+
|
71
|
+
files = ARGV.dup
|
72
|
+
|
73
|
+
includes.each do |path|
|
74
|
+
$LOAD_PATHS.unshift(path)
|
75
|
+
end
|
76
|
+
|
77
|
+
requires.each{ |path| require(path) }
|
78
|
+
|
79
|
+
suite = Lemon::Test::Suite.new(*files)
|
80
|
+
runner = Lemon::Runner.new(suite, format)
|
81
|
+
runner.run
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
data/lib/lemon/coverage.rb
CHANGED
@@ -3,23 +3,31 @@ module Lemon
|
|
3
3
|
#
|
4
4
|
class Coverage
|
5
5
|
|
6
|
-
# Paths of ruby scripts to be covered.
|
7
|
-
|
6
|
+
# Paths of lemon tests and/or ruby scripts to be compared and covered.
|
7
|
+
# This can include directories too, in which case all .rb scripts below
|
8
|
+
# then directory will be included.
|
9
|
+
attr :files
|
8
10
|
|
9
11
|
# Conical snapshot of system (before loading libraries to be covered).
|
10
12
|
attr :conical
|
11
13
|
|
14
|
+
#
|
15
|
+
attr :namespaces
|
16
|
+
|
12
17
|
# New Coverage object.
|
13
18
|
#
|
14
|
-
# Coverage.new('lib/', :public => true)
|
19
|
+
# Coverage.new('lib/', :MyApp, :public => true)
|
15
20
|
#
|
16
|
-
def initialize(
|
17
|
-
@
|
21
|
+
def initialize(files, namespaces=nil, options={})
|
22
|
+
@namespaces = namespaces || []
|
18
23
|
|
19
|
-
@
|
24
|
+
@files = files
|
20
25
|
@conical = snapshot
|
21
26
|
|
22
|
-
|
27
|
+
@public = options[:public]
|
28
|
+
|
29
|
+
# this must come after concial snapshot
|
30
|
+
@suite = Test::Suite.new(files)
|
23
31
|
end
|
24
32
|
|
25
33
|
# Over use public methods for coverage.
|
@@ -60,22 +68,31 @@ module Lemon
|
|
60
68
|
end
|
61
69
|
|
62
70
|
# Iterate over +paths+ and use #load to bring in all +.rb+ scripts.
|
63
|
-
def load_system
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
71
|
+
#def load_system
|
72
|
+
# files = []
|
73
|
+
# paths.map do |path|
|
74
|
+
# if File.directory?(path)
|
75
|
+
# files.concat(Dir[File.join(path, '**', '*.rb')])
|
76
|
+
# else
|
77
|
+
# files.concat(Dir[path])
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
# files.each{ |file| load(file) }
|
81
|
+
#end
|
74
82
|
|
75
83
|
# System to be covered. This takes a sanpshot of the system
|
76
|
-
# and then removes the conical snapshot
|
84
|
+
# and then removes the conical snapshot, and then filters out
|
85
|
+
# the namespace.
|
86
|
+
#
|
87
|
+
# TODO: Perhaps get rid of the conical subtraction and require a namespace?
|
77
88
|
def system
|
78
|
-
|
89
|
+
if namespaces.empty?
|
90
|
+
snapshot - conical
|
91
|
+
else
|
92
|
+
snapshot.select do |m|
|
93
|
+
namespaces.any?{ |n| m.name.start_with?(n) }
|
94
|
+
end
|
95
|
+
end
|
79
96
|
end
|
80
97
|
|
81
98
|
# Produces a list of all existent Modules and Classes.
|
@@ -90,21 +107,23 @@ module Lemon
|
|
90
107
|
sys
|
91
108
|
end
|
92
109
|
|
93
|
-
# TODO: option to do only do what hasn't been covered thus far
|
94
|
-
|
110
|
+
# TODO: combine with coverage to provided option to do only do what hasn't been covered thus far.
|
111
|
+
# TODO: support output directory
|
112
|
+
|
113
|
+
def generate(output=nil)
|
95
114
|
code = []
|
96
115
|
system.each do |base|
|
97
116
|
next if base.is_a?(Lemon::Test::Suite)
|
98
|
-
code << "
|
117
|
+
code << "TestCase #{base} do"
|
99
118
|
base.public_instance_methods(false).each do |meth|
|
100
|
-
code << "\n
|
119
|
+
code << "\n Unit :#{meth} => '' do\n pending\n end"
|
101
120
|
end
|
102
121
|
unless public_only?
|
103
122
|
base.private_instance_methods(false).each do |meth|
|
104
|
-
code << "\n
|
123
|
+
code << "\n Unit :#{meth} => '' do\n pending\n end"
|
105
124
|
end
|
106
125
|
base.protected_instance_methods(false).each do |meth|
|
107
|
-
code << "\n
|
126
|
+
code << "\n Unit :#{meth} => '' do\n pending\n end"
|
108
127
|
end
|
109
128
|
end
|
110
129
|
code << "\nend\n"
|
data/lib/lemon/dsl.rb
ADDED
@@ -0,0 +1,59 @@
|
|
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
|
11
|
+
|
12
|
+
#
|
13
|
+
def Before(match=nil, &block)
|
14
|
+
Lemon.suite.Before(match, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
def After(match=nil, &block)
|
19
|
+
Lemon.suite.After(match, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
def When(match=nil, &block)
|
24
|
+
Lemon.suite.When(match, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
def Case(target_class, &block)
|
29
|
+
Lemon.suite.Case(target_class, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
alias :TestCase :Case
|
33
|
+
|
34
|
+
# FIXME: This is a BIG FAT HACK! For the life of me I cannot find
|
35
|
+
# a way to resolve module constants included in the test cases.
|
36
|
+
# Becuase of closure, the constant lookup goes through here, and not
|
37
|
+
# the Case singleton class. So to work around we must note each test
|
38
|
+
# before it is run, and reroute the missing constants.
|
39
|
+
#
|
40
|
+
# This sucks and it is not thread safe. If anyone know how to fix,
|
41
|
+
# please let me know. See Unit#call for the other end of this hack.
|
42
|
+
|
43
|
+
def Object.const_missing(name)
|
44
|
+
if unit = Lemon.test_stack.last
|
45
|
+
begin
|
46
|
+
(class << unit.testcase; self; end).const_get(name)
|
47
|
+
rescue NameError
|
48
|
+
super(name)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
super(name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Get current running test. Used for the BIG FAT HACK.
|
56
|
+
def Lemon.test_stack
|
57
|
+
@@test_stack ||= []
|
58
|
+
end
|
59
|
+
|
data/lib/lemon/reporter.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
module Lemon
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Reporter Base Class
|
4
4
|
class Reporter
|
5
5
|
|
6
6
|
#
|
7
|
-
def self.factory(format)
|
7
|
+
def self.factory(format, runner)
|
8
8
|
format = format.to_sym if format
|
9
9
|
case format
|
10
10
|
when :verbose
|
11
|
-
Reporters::Verbose.new
|
11
|
+
Reporters::Verbose.new(runner)
|
12
12
|
else
|
13
|
-
Reporters::DotProgress.new
|
13
|
+
Reporters::DotProgress.new(runner)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def initialize(runner)
|
18
|
+
@runner = runner
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
attr :runner
|
23
|
+
|
24
|
+
#
|
17
25
|
def report_start(suite)
|
18
26
|
end
|
19
27
|
|
@@ -21,37 +29,23 @@ module Lemon
|
|
21
29
|
end
|
22
30
|
|
23
31
|
def report_success(testunit)
|
24
|
-
print "."
|
25
32
|
end
|
26
33
|
|
27
34
|
def report_failure(testunit, exception)
|
28
|
-
#puts exception
|
29
|
-
print "F"
|
30
35
|
end
|
31
36
|
|
32
37
|
def report_error(testunit, exception)
|
33
|
-
#puts exception
|
34
|
-
print "E"
|
35
38
|
end
|
36
39
|
|
37
|
-
def report_finish
|
38
|
-
|
40
|
+
def report_finish
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
puts " #{testunit}"
|
42
|
-
puts " #{exception}"
|
43
|
-
puts
|
44
|
-
end
|
43
|
+
private
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
total = successes.size + failures.size + errors.size
|
53
|
-
puts "#{total} tests, #{failures.size} failures, #{errors.size} errors"
|
54
|
-
end
|
45
|
+
def successes ; runner.successes ; end
|
46
|
+
def failures ; runner.failures ; end
|
47
|
+
def errors ; runner.errors ; end
|
48
|
+
def pendings ; runner.pendings ; end
|
55
49
|
|
56
50
|
end
|
57
51
|
|
@@ -6,16 +6,6 @@ module Reporters
|
|
6
6
|
# Generic Reporter
|
7
7
|
class DotProgress < Reporter
|
8
8
|
|
9
|
-
#
|
10
|
-
def self.factory(format)
|
11
|
-
case format.to_sym
|
12
|
-
when :verbose
|
13
|
-
VerboseReporter.new
|
14
|
-
else
|
15
|
-
new
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
9
|
def report_start(suite)
|
20
10
|
end
|
21
11
|
|
@@ -27,32 +17,50 @@ module Reporters
|
|
27
17
|
end
|
28
18
|
|
29
19
|
def report_failure(testunit, exception)
|
30
|
-
#puts exception
|
31
20
|
print "F"
|
32
21
|
end
|
33
22
|
|
34
23
|
def report_error(testunit, exception)
|
35
|
-
#puts exception
|
36
24
|
print "E"
|
37
25
|
end
|
38
26
|
|
39
|
-
def
|
27
|
+
def report_pending(testunit, exception)
|
28
|
+
print "P"
|
29
|
+
end
|
30
|
+
|
31
|
+
def report_finish #(successes, failures, errors, pendings)
|
40
32
|
puts; puts
|
41
33
|
|
42
|
-
failures.
|
43
|
-
puts "
|
44
|
-
|
45
|
-
|
34
|
+
unless failures.empty?
|
35
|
+
puts "FAILURES:\n\n"
|
36
|
+
failures.each do |testunit, exception|
|
37
|
+
puts " #{testunit}"
|
38
|
+
puts " #{exception}"
|
39
|
+
puts " #{exception.backtrace[0]}"
|
40
|
+
puts
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
unless errors.empty?
|
45
|
+
puts "ERRORS:\n\n"
|
46
|
+
errors.each do |testunit, exception|
|
47
|
+
puts " #{testunit}"
|
48
|
+
puts " #{exception}"
|
49
|
+
puts " #{exception.backtrace[0]}"
|
50
|
+
puts
|
51
|
+
end
|
46
52
|
end
|
47
53
|
|
48
|
-
|
49
|
-
puts "
|
50
|
-
|
54
|
+
unless pendings.empty?
|
55
|
+
puts "PENDING:\n\n"
|
56
|
+
pendings.each do |testunit, exception|
|
57
|
+
puts " #{testunit}"
|
58
|
+
end
|
51
59
|
puts
|
52
60
|
end
|
53
61
|
|
54
|
-
total = successes.size + failures.size + errors.size
|
55
|
-
puts "#{total} tests, #{failures.size} failures, #{errors.size} errors"
|
62
|
+
total = successes.size + failures.size + errors.size + pendings.size
|
63
|
+
puts "#{total} tests, #{successes.size} pass, #{failures.size} failures, #{errors.size} errors, #{pendings.size} pending"
|
56
64
|
end
|
57
65
|
|
58
66
|
end
|
@@ -13,43 +13,76 @@ module Reporters
|
|
13
13
|
#
|
14
14
|
def report_concern(concern)
|
15
15
|
puts
|
16
|
-
puts concern.description
|
16
|
+
puts "#{concern.description}\n\n" unless concern.description.empty?
|
17
17
|
end
|
18
18
|
|
19
19
|
#
|
20
20
|
def report_success(testunit)
|
21
|
-
puts "*
|
21
|
+
puts "* #{testunit}"
|
22
22
|
end
|
23
23
|
|
24
24
|
#
|
25
25
|
def report_failure(testunit, exception)
|
26
|
-
puts "*
|
27
|
-
|
26
|
+
puts "* #{testunit}"
|
27
|
+
puts
|
28
|
+
puts " FAIL #{exception.backtrace[0]}"
|
29
|
+
puts " #{exception}"
|
30
|
+
puts
|
28
31
|
end
|
29
32
|
|
30
33
|
#
|
31
34
|
def report_error(testunit, exception)
|
32
|
-
puts "*
|
33
|
-
|
35
|
+
puts "* #{testunit}"
|
36
|
+
puts
|
37
|
+
puts " ERROR #{exception.backtrace[0]}"
|
38
|
+
puts " #{exception}"
|
39
|
+
puts
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def report_pending(testunit, exception)
|
44
|
+
puts "* #{testunit}"
|
45
|
+
puts
|
46
|
+
puts " PENDING #{exception.backtrace[1]}"
|
47
|
+
puts
|
34
48
|
end
|
35
49
|
|
36
|
-
|
37
|
-
|
50
|
+
#
|
51
|
+
def report_finish
|
52
|
+
puts
|
38
53
|
|
39
|
-
|
40
|
-
|
41
|
-
puts "
|
42
|
-
|
54
|
+
=begin
|
55
|
+
unless failures.empty?
|
56
|
+
puts "FAILURES:\n\n"
|
57
|
+
failures.each do |testunit, exception|
|
58
|
+
puts " #{testunit}"
|
59
|
+
puts " #{exception}"
|
60
|
+
puts " #{exception.backtrace[0]}"
|
61
|
+
puts
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
unless errors.empty?
|
66
|
+
puts "ERRORS:\n\n"
|
67
|
+
errors.each do |testunit, exception|
|
68
|
+
puts " #{testunit}"
|
69
|
+
puts " #{exception}"
|
70
|
+
puts " #{exception.backtrace[0]}"
|
71
|
+
puts
|
72
|
+
end
|
43
73
|
end
|
44
74
|
|
45
|
-
|
46
|
-
puts "
|
47
|
-
|
75
|
+
unless pendings.empty?
|
76
|
+
puts "PENDING:\n\n"
|
77
|
+
pendings.each do |testunit, exception|
|
78
|
+
puts " #{testunit}"
|
79
|
+
end
|
48
80
|
puts
|
49
81
|
end
|
82
|
+
=end
|
50
83
|
|
51
|
-
total = successes.size + failures.size + errors.size
|
52
|
-
puts "#{total} tests, #{failures.size} failures, #{errors.size} errors"
|
84
|
+
total = successes.size + failures.size + errors.size + pendings.size
|
85
|
+
puts "#{total} tests, #{successes.size} pass, #{failures.size} failures, #{errors.size} errors, #{pendings.size} pending"
|
53
86
|
end
|
54
87
|
|
55
88
|
end
|
data/lib/lemon/runner.rb
CHANGED
@@ -14,6 +14,9 @@ module Lemon
|
|
14
14
|
# Test suite to run.
|
15
15
|
attr :suite
|
16
16
|
|
17
|
+
# Report format.
|
18
|
+
attr :format
|
19
|
+
|
17
20
|
# Record of successful tests.
|
18
21
|
attr :successes
|
19
22
|
|
@@ -23,8 +26,8 @@ module Lemon
|
|
23
26
|
# Record of errors.
|
24
27
|
attr :errors
|
25
28
|
|
26
|
-
#
|
27
|
-
attr :
|
29
|
+
# Record of pending tests.
|
30
|
+
attr :pendings
|
28
31
|
|
29
32
|
# New Runner.
|
30
33
|
def initialize(suite, format)
|
@@ -33,6 +36,7 @@ module Lemon
|
|
33
36
|
@successes = []
|
34
37
|
@failures = []
|
35
38
|
@errors = []
|
39
|
+
@pendings = []
|
36
40
|
end
|
37
41
|
|
38
42
|
# Run tests.
|
@@ -48,6 +52,9 @@ module Lemon
|
|
48
52
|
testunit.call
|
49
53
|
reporter.report_success(testunit)
|
50
54
|
successes << testunit
|
55
|
+
rescue PendingAssertion => exception
|
56
|
+
reporter.report_pending(testunit, exception)
|
57
|
+
pendings << [testunit, exception]
|
51
58
|
rescue Assertion => exception
|
52
59
|
reporter.report_failure(testunit, exception)
|
53
60
|
failures << [testunit, exception]
|
@@ -59,12 +66,12 @@ module Lemon
|
|
59
66
|
end
|
60
67
|
end
|
61
68
|
end
|
62
|
-
reporter.report_finish(successes, failures, errors)
|
69
|
+
reporter.report_finish #(successes, failures, errors, pendings)
|
63
70
|
end
|
64
71
|
|
65
72
|
# All output is handled by a reporter.
|
66
73
|
def reporter
|
67
|
-
@reporter ||= Reporter.factory(format)
|
74
|
+
@reporter ||= Reporter.factory(format, self)
|
68
75
|
end
|
69
76
|
|
70
77
|
private
|
data/lib/lemon/test/case.rb
CHANGED
@@ -99,6 +99,11 @@ module Lemon::Test
|
|
99
99
|
@when_clauses[match] = block #<< Advice.new(match, &block)
|
100
100
|
end
|
101
101
|
|
102
|
+
#
|
103
|
+
def pending
|
104
|
+
raise PendingAssertion
|
105
|
+
end
|
106
|
+
|
102
107
|
#
|
103
108
|
def to_s
|
104
109
|
target.to_s.sub(/^\#\<.*?\>::/, '')
|
@@ -107,3 +112,6 @@ module Lemon::Test
|
|
107
112
|
|
108
113
|
end
|
109
114
|
|
115
|
+
class PendingAssertion < Assertion
|
116
|
+
end
|
117
|
+
|
data/lib/lemon/test/concern.rb
CHANGED
data/lib/lemon/test/suite.rb
CHANGED
@@ -2,6 +2,7 @@ module Lemon
|
|
2
2
|
module Test
|
3
3
|
|
4
4
|
require 'lemon/test/case'
|
5
|
+
require 'lemon/dsl'
|
5
6
|
|
6
7
|
# Test Suites encapsulate a set of test cases.
|
7
8
|
#
|
@@ -19,18 +20,22 @@ module Test
|
|
19
20
|
# List of post-test procedures that apply suite-wide.
|
20
21
|
attr :after_clauses
|
21
22
|
|
22
|
-
# List of concern procedures that apply suite-wide.
|
23
|
-
attr :when_clauses
|
24
|
-
|
25
23
|
#
|
26
|
-
def initialize(*
|
24
|
+
def initialize(*files)
|
27
25
|
@testcases = []
|
28
26
|
@before_clauses = {}
|
29
27
|
@after_clauses = {}
|
30
28
|
@when_clauses = {}
|
31
29
|
|
30
|
+
loadfiles(*files)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
def loadfiles(*files)
|
35
|
+
Lemon.suite = self
|
36
|
+
|
32
37
|
# directories glob *.rb files
|
33
|
-
|
38
|
+
files = files.flatten.map do |file|
|
34
39
|
if File.directory?(file)
|
35
40
|
Dir[File.join(file, '**', '*.rb')]
|
36
41
|
else
|
@@ -38,12 +43,13 @@ module Test
|
|
38
43
|
end
|
39
44
|
end.flatten.uniq
|
40
45
|
|
41
|
-
|
42
|
-
|
43
|
-
tests.each do |file|
|
46
|
+
files.each do |file|
|
44
47
|
#file = File.expand_path(file)
|
45
|
-
instance_eval(File.read(file), file)
|
48
|
+
#instance_eval(File.read(file), file)
|
49
|
+
load(file)
|
46
50
|
end
|
51
|
+
|
52
|
+
return Lemon.suite
|
47
53
|
end
|
48
54
|
|
49
55
|
# Load a helper. This method must be used when loading local
|
@@ -75,21 +81,21 @@ module Test
|
|
75
81
|
alias_method :TestCase, :Case
|
76
82
|
|
77
83
|
#
|
78
|
-
alias_method :testcase, :Case
|
84
|
+
#alias_method :testcase, :Case
|
79
85
|
|
80
86
|
# Define a pre-test procedure to apply suite-wide.
|
81
87
|
def Before(match=nil, &block)
|
82
88
|
@before_clauses[match] = block #<< Advice.new(match, &block)
|
83
89
|
end
|
84
90
|
|
85
|
-
alias_method :before, :Before
|
91
|
+
#alias_method :before, :Before
|
86
92
|
|
87
93
|
# Define a post-test procedure to apply suite-wide.
|
88
94
|
def After(match=nil, &block)
|
89
95
|
@after_clauses[match] = block #<< Advice.new(match, &block)
|
90
96
|
end
|
91
97
|
|
92
|
-
alias_method :after, :After
|
98
|
+
#alias_method :after, :After
|
93
99
|
|
94
100
|
# Define a concern procedure to apply suite-wide.
|
95
101
|
def When(match=nil, &block)
|
@@ -101,24 +107,6 @@ module Test
|
|
101
107
|
@testcases.each(&block)
|
102
108
|
end
|
103
109
|
|
104
|
-
# FIXME: This is a BIG FAT HACK! For the life of me I cannot find
|
105
|
-
# a way to resolve module constants included in the test cases.
|
106
|
-
# Becuase of closure, the constant lookup goes through here, and not
|
107
|
-
# the Case singleton class. So to work around wemust note each test
|
108
|
-
# before it is run, and reroute the missing constants.
|
109
|
-
#
|
110
|
-
# This sucks and it is not thread safe. If anyone know how to fix,
|
111
|
-
# please let me know. See Unit#call for the other end of this hack.
|
112
|
-
|
113
|
-
def self.const_missing(name)
|
114
|
-
(class << @@test_stack.last.testcase; self; end).const_get(name)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Get current running test. Used for the BIG FAT HACK.
|
118
|
-
def test_stack
|
119
|
-
@@test_stack ||= []
|
120
|
-
end
|
121
|
-
|
122
110
|
end
|
123
111
|
|
124
112
|
end
|
data/lib/lemon/test/unit.rb
CHANGED
@@ -32,10 +32,10 @@ module Lemon::Test
|
|
32
32
|
|
33
33
|
# This method has the other end of the BIG FAT HACK. See Suite#const_missing.
|
34
34
|
def call
|
35
|
-
|
35
|
+
Lemon.test_stack << self # hack
|
36
36
|
procedure.call
|
37
37
|
ensure
|
38
|
-
|
38
|
+
Lemon.test_stack.pop
|
39
39
|
end
|
40
40
|
|
41
41
|
# The suite to which this unit test belongs.
|
data/meta/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6
|
data/test/cases/testcase_case.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lemon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.6"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Sawyer
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-03 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -32,11 +32,16 @@ files:
|
|
32
32
|
- COPYING
|
33
33
|
- COPYING.LESSER
|
34
34
|
- HISTORY
|
35
|
+
- MANIFEST
|
35
36
|
- README.rdoc
|
36
37
|
- bin/lemon
|
37
38
|
- lib/lemon.rb
|
38
39
|
- lib/lemon/command.rb
|
40
|
+
- lib/lemon/commands/coverage.rb
|
41
|
+
- lib/lemon/commands/generate.rb
|
42
|
+
- lib/lemon/commands/test.rb
|
39
43
|
- lib/lemon/coverage.rb
|
44
|
+
- lib/lemon/dsl.rb
|
40
45
|
- lib/lemon/kernel.rb
|
41
46
|
- lib/lemon/reporter.rb
|
42
47
|
- lib/lemon/reporters.rb
|
@@ -61,7 +66,6 @@ files:
|
|
61
66
|
- meta/version
|
62
67
|
- test/cases/coverage_case.rb
|
63
68
|
- test/cases/testcase_case.rb
|
64
|
-
- MANIFEST
|
65
69
|
- Syckfile
|
66
70
|
has_rdoc: true
|
67
71
|
homepage: http://proutils.github.com/lemon
|
@@ -93,4 +97,5 @@ signing_key:
|
|
93
97
|
specification_version: 3
|
94
98
|
summary: Pucker-tight Unit Testing
|
95
99
|
test_files:
|
100
|
+
- lib/lemon/commands/test.rb
|
96
101
|
- test/cases/testcase_case.rb
|