lemon 0.5

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.
@@ -0,0 +1,118 @@
1
+ module Lemon
2
+
3
+ #
4
+ class Coverage
5
+
6
+ # Paths of ruby scripts to be covered.
7
+ attr :paths
8
+
9
+ # Conical snapshot of system (before loading libraries to be covered).
10
+ attr :conical
11
+
12
+ # New Coverage object.
13
+ #
14
+ # Coverage.new('lib/', :public => true)
15
+ #
16
+ def initialize(paths, options={})
17
+ @public = options[:public]
18
+
19
+ @paths = paths
20
+ @conical = snapshot
21
+
22
+ load_system
23
+ end
24
+
25
+ # Over use public methods for coverage.
26
+ def public_only?
27
+ @public
28
+ end
29
+
30
+ # Produce a coverage map.
31
+ def coverage(suite)
32
+ checklist = cover()
33
+ suite.each do |testcase|
34
+ testcase.testunits.each do |testunit|
35
+ checklist[testcase.target.name][testunit.target.to_s] = true
36
+ end
37
+ end
38
+ checklist
39
+ end
40
+
41
+ # Coverage template.
42
+ def cover
43
+ cover = Hash.new{|h,k|h[k]={}}
44
+ system.each do |base|
45
+ next if base.is_a?(Lemon::Test::Suite)
46
+ cover[base.name] = {}
47
+ base.public_instance_methods(false).each do |meth|
48
+ cover[base.name][meth.to_s] = false
49
+ end
50
+ unless public_only?
51
+ base.private_instance_methods(false).each do |meth|
52
+ cover[base.name][meth.to_s] = false
53
+ end
54
+ base.protected_instance_methods(false).each do |meth|
55
+ cover[base.name][meth.to_s] = false
56
+ end
57
+ end
58
+ end
59
+ cover
60
+ end
61
+
62
+ # 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
74
+
75
+ # System to be covered. This takes a sanpshot of the system
76
+ # and then removes the conical snapshot.
77
+ def system
78
+ snapshot - conical
79
+ end
80
+
81
+ # Produces a list of all existent Modules and Classes.
82
+ def snapshot
83
+ sys = []
84
+ #ObjectSpace.each_object(Class) do |c|
85
+ # sys << c
86
+ #end
87
+ ObjectSpace.each_object(Module) do |m|
88
+ sys << m
89
+ end
90
+ sys
91
+ end
92
+
93
+ # TODO: option to do only do what hasn't been covered thus far
94
+ def generate(opts={})
95
+ code = []
96
+ system.each do |base|
97
+ next if base.is_a?(Lemon::Test::Suite)
98
+ code << "testcase #{base}"
99
+ base.public_instance_methods(false).each do |meth|
100
+ code << "\n unit :#{meth} => '' do\n pending\n end"
101
+ end
102
+ unless public_only?
103
+ base.private_instance_methods(false).each do |meth|
104
+ code << "\n unit :#{meth} => '' do\n pending\n end"
105
+ end
106
+ base.protected_instance_methods(false).each do |meth|
107
+ code << "\n unit :#{meth} => '' do\n pending\n end"
108
+ end
109
+ end
110
+ code << "\nend\n"
111
+ end
112
+ code.join("\n")
113
+ end
114
+
115
+ end#class Coverage
116
+
117
+ end#module Lemon
118
+
@@ -0,0 +1,20 @@
1
+ require 'facets/functor'
2
+
3
+ $PRY_TABLE = {} #Hash.new{|h,k| h[k]=nil}
4
+
5
+ # Pry allows you to test private and protected methods,
6
+ # via a public-only interface.
7
+ #
8
+ # Generally one should avoid testing private and protected
9
+ # method directly, instead relying on tests of public methods to
10
+ # indirectly test them, because private and protected methods are
11
+ # considered implementation details. But sometimes is necessary
12
+ # to test them directly, or if you wish to achieve *absolute
13
+ # coverage*, say in mission critical systems.
14
+
15
+ def pry
16
+ $PRY_TABLE[self] ||= Functor.new do |op, *a, &b|
17
+ __send__(op, *a, &b)
18
+ end
19
+ end
20
+
@@ -0,0 +1,58 @@
1
+ module Lemon
2
+
3
+ # Generic Reporter
4
+ class Reporter
5
+
6
+ #
7
+ def self.factory(format)
8
+ format = format.to_sym if format
9
+ case format
10
+ when :verbose
11
+ Reporters::Verbose.new
12
+ else
13
+ Reporters::DotProgress.new
14
+ end
15
+ end
16
+
17
+ def report_start(suite)
18
+ end
19
+
20
+ def report_concern(concern)
21
+ end
22
+
23
+ def report_success(testunit)
24
+ print "."
25
+ end
26
+
27
+ def report_failure(testunit, exception)
28
+ #puts exception
29
+ print "F"
30
+ end
31
+
32
+ def report_error(testunit, exception)
33
+ #puts exception
34
+ print "E"
35
+ end
36
+
37
+ def report_finish(successes, failures, errors)
38
+ puts; puts
39
+
40
+ failures.each do |testunit, exception|
41
+ puts " #{testunit}"
42
+ puts " #{exception}"
43
+ puts
44
+ end
45
+
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
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,3 @@
1
+ require 'lemon/reporters/dotprogress'
2
+ require 'lemon/reporters/verbose'
3
+
@@ -0,0 +1,62 @@
1
+ module Lemon
2
+ module Reporters
3
+
4
+ require 'lemon/reporter'
5
+
6
+ # Generic Reporter
7
+ class DotProgress < Reporter
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
+ def report_start(suite)
20
+ end
21
+
22
+ def report_concern(concern)
23
+ end
24
+
25
+ def report_success(testunit)
26
+ print "."
27
+ end
28
+
29
+ def report_failure(testunit, exception)
30
+ #puts exception
31
+ print "F"
32
+ end
33
+
34
+ def report_error(testunit, exception)
35
+ #puts exception
36
+ print "E"
37
+ end
38
+
39
+ def report_finish(successes, failures, errors)
40
+ puts; puts
41
+
42
+ failures.each do |testunit, exception|
43
+ puts " #{testunit}"
44
+ puts " #{exception}"
45
+ puts
46
+ end
47
+
48
+ errors.each do |testunit, exception|
49
+ puts " #{testunit}"
50
+ puts " #{exception}"
51
+ puts
52
+ end
53
+
54
+ total = successes.size + failures.size + errors.size
55
+ puts "#{total} tests, #{failures.size} failures, #{errors.size} errors"
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
62
+
@@ -0,0 +1,59 @@
1
+ module Lemon
2
+ module Reporters
3
+
4
+ require 'lemon/reporter'
5
+
6
+ # Generic Reporter
7
+ class Verbose < Reporter
8
+
9
+ #
10
+ def report_start(suite)
11
+ end
12
+
13
+ #
14
+ def report_concern(concern)
15
+ puts
16
+ puts concern.description
17
+ end
18
+
19
+ #
20
+ def report_success(testunit)
21
+ puts "* [PASS] #{testunit}"
22
+ end
23
+
24
+ #
25
+ def report_failure(testunit, exception)
26
+ puts "* [FAIL] #{testunit.target}"
27
+ #puts exception
28
+ end
29
+
30
+ #
31
+ def report_error(testunit, exception)
32
+ puts "* [ERROR] #{testunit.target}"
33
+ #puts exception
34
+ end
35
+
36
+ def report_finish(successes, failures, errors)
37
+ puts; puts
38
+
39
+ failures.each do |testunit, exception|
40
+ puts " #{testunit}"
41
+ puts " #{exception}"
42
+ puts
43
+ end
44
+
45
+ errors.each do |testunit, exception|
46
+ puts " #{testunit}"
47
+ puts " #{exception}"
48
+ puts
49
+ end
50
+
51
+ total = successes.size + failures.size + errors.size
52
+ puts "#{total} tests, #{failures.size} failures, #{errors.size} errors"
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
59
+
@@ -0,0 +1,117 @@
1
+ module Lemon
2
+
3
+ require 'ae'
4
+ require 'ae/expect'
5
+ require 'ae/should'
6
+
7
+ require 'lemon/kernel'
8
+ require 'lemon/test/suite'
9
+ require 'lemon/reporters'
10
+
11
+ #
12
+ class Runner
13
+
14
+ # Test suite to run.
15
+ attr :suite
16
+
17
+ # Record of successful tests.
18
+ attr :successes
19
+
20
+ # Record of failed tests.
21
+ attr :failures
22
+
23
+ # Record of errors.
24
+ attr :errors
25
+
26
+ # Report format.
27
+ attr :format
28
+
29
+ # New Runner.
30
+ def initialize(suite, format)
31
+ @suite = suite
32
+ @format = format
33
+ @successes = []
34
+ @failures = []
35
+ @errors = []
36
+ end
37
+
38
+ # Run tests.
39
+ def run
40
+ reporter.report_start(suite)
41
+ suite.each do |testcase|
42
+ testcase.each do |concern|
43
+ reporter.report_concern(concern)
44
+ run_concern_procedures(concern, suite, testcase)
45
+ concern.each do |testunit|
46
+ run_pretest_procedures(testunit, suite, testcase)
47
+ begin
48
+ testunit.call
49
+ reporter.report_success(testunit)
50
+ successes << testunit
51
+ rescue Assertion => exception
52
+ reporter.report_failure(testunit, exception)
53
+ failures << [testunit, exception]
54
+ rescue Exception => exception
55
+ reporter.report_error(testunit, exception)
56
+ errors << [testunit, exception]
57
+ end
58
+ run_postest_procedures(testunit, suite, testcase)
59
+ end
60
+ end
61
+ end
62
+ reporter.report_finish(successes, failures, errors)
63
+ end
64
+
65
+ # All output is handled by a reporter.
66
+ def reporter
67
+ @reporter ||= Reporter.factory(format)
68
+ end
69
+
70
+ private
71
+
72
+ #
73
+ def run_concern_procedures(concern, suite, testcase)
74
+ suite.when_clauses.each do |match, block|
75
+ if match.nil? or match === concern.to_s
76
+ block.call(testcase)
77
+ end
78
+ end
79
+ testcase.when_clauses.each do |match, block|
80
+ if match.nil? or match === concern.to_s
81
+ block.call(testcase) if match === concern.to_s
82
+ end
83
+ end
84
+ end
85
+
86
+ #
87
+ def run_pretest_procedures(testunit, suite, testcase)
88
+ suite.before_clauses.each do |match, block|
89
+ if match.nil? or match === testunit.aspect
90
+ block.call(testunit)
91
+ end
92
+ end
93
+ testcase.before_clauses.each do |match, block|
94
+ if match.nil? or match === testunit.aspect
95
+ block.call(testunit)
96
+ end
97
+ end
98
+ end
99
+
100
+ #
101
+ def run_postest_procedures(testunit, suite, testcase)
102
+ testcase.after_clauses.each do |match, block|
103
+ if match.nil? or match === testunit.aspect
104
+ block.call(testunit)
105
+ end
106
+ end
107
+ suite.after_clauses.each do |match, block|
108
+ if match.nil? or match === testunit.aspect
109
+ block.call(testunit)
110
+ end
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ end
117
+
@@ -0,0 +1,109 @@
1
+ module Lemon::Test
2
+
3
+ require 'lemon/test/concern'
4
+ require 'lemon/test/unit'
5
+
6
+ # Test Case encapsulates a collection of
7
+ # unit tests organized into groups of concern.
8
+ class Case
9
+
10
+ # The test suite to which this testcase belongs.
11
+ attr :suite
12
+
13
+ # A testcase +target+ is a class or module.
14
+ attr :target
15
+
16
+ #
17
+ attr :testunits
18
+
19
+ # List of before procedures that apply case-wide.
20
+ attr :before_clauses
21
+
22
+ # List of after procedures that apply case-wide.
23
+ attr :after_clauses
24
+
25
+ # List of concern procedures that apply case-wide.
26
+ attr :when_clauses
27
+
28
+ # A test case +target+ is a class or module.
29
+ def initialize(suite, target, &block)
30
+ @suite = suite
31
+ @target = target
32
+ @testunits = []
33
+ @concerns = []
34
+ @before_clauses = {}
35
+ @after_clauses = {}
36
+ @when_clauses = {}
37
+ instance_eval(&block)
38
+ end
39
+
40
+ # Load a helper script applicable to this test case.
41
+ def helper(file)
42
+ instance_eval(File.read(file), file)
43
+ end
44
+
45
+ # NOTE: Due to a limitation in Ruby this does not
46
+ # provived access to submodules. A hack has been used
47
+ # to circumvent. See Suite.const_missing.
48
+ def include(*mods)
49
+ extend *mods
50
+ end
51
+
52
+ # Define a new test concern for this case.
53
+ def Concern(*description)
54
+ concern = Concern.new(self, description)
55
+ @concerns << concern
56
+ end
57
+
58
+ alias_method :concern, :Concern
59
+
60
+ # The last defined concern. Used to assign new unit tests.
61
+ def current_concern
62
+ if @concerns.empty?
63
+ @concerns << Concern.new(self)
64
+ end
65
+ @concerns.last
66
+ end
67
+
68
+ # Iterate over each test concern.
69
+ def each(&block)
70
+ @concerns.each(&block)
71
+ end
72
+
73
+ # Define a unit test for this case.
74
+ def Unit(*targets, &block)
75
+ targets_hash = Hash===targets.last ? targets.pop : {}
76
+ targets_hash.each do |target_method, target_concern|
77
+ @testunits << Unit.new(current_concern, target_method, target_concern, &block)
78
+ end
79
+ targets.each do |target_method|
80
+ @testunits << Unit.new(current_concern, target_method, &block)
81
+ end
82
+ end
83
+ alias_method :unit, :Unit
84
+
85
+ # Define a before procedure for this case.
86
+ def Before(match=nil, &block)
87
+ @before_clauses[match] = block #<< Advice.new(match, &block)
88
+ end
89
+ alias_method :before, :Before
90
+
91
+ # Define an after procedure for this case.
92
+ def After(match=nil, &block)
93
+ @after_clauses[match] = block #<< Advice.new(match, &block)
94
+ end
95
+ alias_method :after, :After
96
+
97
+ # Define a concern procedure to apply case-wide.
98
+ def When(match=nil, &block)
99
+ @when_clauses[match] = block #<< Advice.new(match, &block)
100
+ end
101
+
102
+ #
103
+ def to_s
104
+ target.to_s.sub(/^\#\<.*?\>::/, '')
105
+ end
106
+ end
107
+
108
+ end
109
+