lemon 0.5

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