lemon 0.5 → 0.6

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