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 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
@@ -1,125 +1,45 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'lemon'
4
- require 'lemon/coverage'
5
1
  require 'optparse'
6
- require 'yaml'
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
- # Initialize and run.
14
- def self.run
15
- new.run
16
- end
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 public_only?
68
- @public_only
18
+ # Stores a list of command classes.
19
+ def self.commands
20
+ @commands ||= []
69
21
  end
70
22
 
71
- #
72
- def requires=(paths)
73
- @requires = paths.split(/[:;]/)
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 includes=(paths)
78
- @includes = paths.split(/[:;]/)
79
- end
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
- end
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
+
@@ -3,23 +3,31 @@ module Lemon
3
3
  #
4
4
  class Coverage
5
5
 
6
- # Paths of ruby scripts to be covered.
7
- attr :paths
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(paths, options={})
17
- @public = options[:public]
21
+ def initialize(files, namespaces=nil, options={})
22
+ @namespaces = namespaces || []
18
23
 
19
- @paths = paths
24
+ @files = files
20
25
  @conical = snapshot
21
26
 
22
- load_system
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
- files = []
65
- paths.map do |path|
66
- if File.directory?(path)
67
- files.concat(Dir[File.join(path, '**', '*.rb')])
68
- else
69
- files.concat(Dir[path])
70
- end
71
- end
72
- files.each{ |file| load(file) }
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
- snapshot - conical
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
- def generate(opts={})
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 << "testcase #{base}"
117
+ code << "TestCase #{base} do"
99
118
  base.public_instance_methods(false).each do |meth|
100
- code << "\n unit :#{meth} => '' do\n pending\n end"
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 unit :#{meth} => '' do\n pending\n end"
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 unit :#{meth} => '' do\n pending\n end"
126
+ code << "\n Unit :#{meth} => '' do\n pending\n end"
108
127
  end
109
128
  end
110
129
  code << "\nend\n"
@@ -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
+
@@ -1,19 +1,27 @@
1
1
  module Lemon
2
2
 
3
- # Generic Reporter
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(successes, failures, errors)
38
- puts; puts
40
+ def report_finish
41
+ end
39
42
 
40
- failures.each do |testunit, exception|
41
- puts " #{testunit}"
42
- puts " #{exception}"
43
- puts
44
- end
43
+ private
45
44
 
46
- errors.each do |testunit, exception|
47
- puts " #{testunit}"
48
- puts " #{exception}"
49
- puts
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 report_finish(successes, failures, errors)
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.each do |testunit, exception|
43
- puts " #{testunit}"
44
- puts " #{exception}"
45
- puts
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
- errors.each do |testunit, exception|
49
- puts " #{testunit}"
50
- puts " #{exception}"
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 "* [PASS] #{testunit}"
21
+ puts "* #{testunit}"
22
22
  end
23
23
 
24
24
  #
25
25
  def report_failure(testunit, exception)
26
- puts "* [FAIL] #{testunit.target}"
27
- #puts exception
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 "* [ERROR] #{testunit.target}"
33
- #puts exception
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
- def report_finish(successes, failures, errors)
37
- puts; puts
50
+ #
51
+ def report_finish
52
+ puts
38
53
 
39
- failures.each do |testunit, exception|
40
- puts " #{testunit}"
41
- puts " #{exception}"
42
- puts
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
- errors.each do |testunit, exception|
46
- puts " #{testunit}"
47
- puts " #{exception}"
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
@@ -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
- # Report format.
27
- attr :format
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
@@ -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
+
@@ -16,7 +16,7 @@ module Lemon::Test
16
16
  attr :testunits
17
17
 
18
18
  # New concern.
19
- def initialize(testcase, description)
19
+ def initialize(testcase, *description)
20
20
  @testcase = testcase
21
21
  @description = description.join("\n")
22
22
  @testunits = []
@@ -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(*tests)
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
- tests = tests.flatten.map do |file|
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
- @test_files = tests
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
@@ -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
- suite.test_stack << self # hack
35
+ Lemon.test_stack << self # hack
36
36
  procedure.call
37
37
  ensure
38
- suite.test_stack.pop
38
+ Lemon.test_stack.pop
39
39
  end
40
40
 
41
41
  # The suite to which this unit test belongs.
@@ -1 +1 @@
1
- 0.5
1
+ 0.6
@@ -6,7 +6,7 @@ module M
6
6
  end
7
7
  end
8
8
 
9
- Case(Lemon::Test::Case) do
9
+ TestCase(Lemon::Test::Case) do
10
10
 
11
11
  Concern "Modules included in a test case are accessible by the unit tests."
12
12
 
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.5"
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: 2009-12-31 00:00:00 -05:00
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