lemon 0.8.1 → 0.8.2
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/HISTORY.rdoc +15 -0
- data/README.rdoc +32 -14
- data/bin/lemon +3 -2
- data/demo/case_example_fail.rb +15 -0
- data/demo/case_example_pass.rb +32 -0
- data/demo/case_example_pending.rb +14 -0
- data/demo/case_example_untested.rb +10 -0
- data/demo/fixture/example-use.rb +5 -0
- data/demo/fixture/example.rb +20 -0
- data/lib/lemon.rb +2 -2
- data/lib/lemon/cli.rb +281 -0
- data/lib/lemon/controller/coverage_analyzer.rb +343 -0
- data/lib/lemon/controller/scaffold_generator.rb +110 -0
- data/lib/lemon/controller/test_runner.rb +284 -0
- data/lib/lemon/meta/data.rb +29 -0
- data/lib/lemon/meta/gemfile +24 -0
- data/{PROFILE → lib/lemon/meta/profile} +6 -5
- data/lib/lemon/model/ae.rb +4 -0
- data/lib/lemon/model/cover_unit.rb +75 -0
- data/lib/lemon/{dsl.rb → model/main.rb} +22 -28
- data/lib/lemon/model/pending.rb +10 -0
- data/lib/lemon/model/snapshot.rb +203 -0
- data/lib/lemon/model/source_parser.rb +198 -0
- data/lib/lemon/model/test_case.rb +221 -0
- data/lib/lemon/model/test_context.rb +90 -0
- data/lib/lemon/model/test_suite.rb +216 -0
- data/lib/lemon/{test/unit.rb → model/test_unit.rb} +40 -28
- data/lib/lemon/{coversheet → view/cover_reports}/abstract.rb +19 -20
- data/lib/lemon/view/cover_reports/compact.rb +37 -0
- data/lib/lemon/view/cover_reports/outline.rb +45 -0
- data/lib/lemon/view/cover_reports/verbose.rb +51 -0
- data/lib/lemon/view/cover_reports/yaml.rb +15 -0
- data/lib/lemon/view/test_reports/abstract.rb +149 -0
- data/lib/lemon/view/test_reports/dotprogress.rb +73 -0
- data/lib/lemon/view/test_reports/html.rb +146 -0
- data/lib/lemon/view/test_reports/outline.rb +118 -0
- data/lib/lemon/view/test_reports/summary.rb +131 -0
- data/lib/lemon/view/test_reports/tap.rb +49 -0
- data/lib/lemon/view/test_reports/verbose.rb +197 -0
- data/meta/data.rb +29 -0
- data/meta/gemfile +24 -0
- data/meta/profile +17 -0
- data/test/api/applique/fs.rb +18 -0
- data/test/api/coverage/complete.rdoc +136 -0
- data/test/api/coverage/extensions.rdoc +61 -0
- data/test/api/coverage/incomplete.rdoc +97 -0
- data/{features → test/cli}/coverage.feature +4 -4
- data/{features → test/cli}/generate.feature +2 -2
- data/{features → test/cli}/step_definitions/coverage_steps.rb +0 -0
- data/{features → test/cli}/support/ae.rb +0 -0
- data/{features → test/cli}/support/aruba.rb +0 -0
- data/{features → test/cli}/test.feature +0 -0
- data/test/fixtures/case_complete.rb +17 -4
- data/test/fixtures/case_inclusion.rb +18 -0
- data/test/fixtures/case_incomplete.rb +4 -4
- data/test/fixtures/example.rb +5 -0
- data/test/fixtures/helper.rb +13 -0
- data/test/runner +3 -0
- data/test/unit/case_coverage_analyzer.rb +25 -0
- data/test/unit/case_test_case_dsl.rb +46 -0
- metadata +87 -42
- data/REQUIRE +0 -9
- data/VERSION +0 -6
- data/lib/lemon/command.rb +0 -184
- data/lib/lemon/coverage.rb +0 -260
- data/lib/lemon/coversheet/outline.rb +0 -47
- data/lib/lemon/kernel.rb +0 -24
- data/lib/lemon/reporter.rb +0 -22
- data/lib/lemon/reporter/abstract.rb +0 -97
- data/lib/lemon/reporter/dotprogress.rb +0 -68
- data/lib/lemon/reporter/outline.rb +0 -105
- data/lib/lemon/reporter/verbose.rb +0 -143
- data/lib/lemon/runner.rb +0 -308
- data/lib/lemon/snapshot.rb +0 -185
- data/lib/lemon/test/case.rb +0 -139
- data/lib/lemon/test/concern.rb +0 -52
- data/lib/lemon/test/suite.rb +0 -229
- data/test/case_coverage.rb +0 -26
- data/test/case_testcase.rb +0 -58
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'lemon/view/cover_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::CoverReports
|
4
|
+
|
5
|
+
class Compact < Abstract
|
6
|
+
|
7
|
+
#
|
8
|
+
def render
|
9
|
+
|
10
|
+
unless covered_units.empty?
|
11
|
+
puts "\nCovered Units: "
|
12
|
+
puts covered_units.map{ |u| u.to_s.ansi(:green) }.sort.join(', ')
|
13
|
+
end
|
14
|
+
|
15
|
+
unless undefined_units.empty?
|
16
|
+
puts "\nOvercovered Units:"
|
17
|
+
puts undefined_units.map{ |u| u.to_s.ansi(:cyan) }.sort.join(', ')
|
18
|
+
end
|
19
|
+
|
20
|
+
unless uncovered_units.empty?
|
21
|
+
puts "\nUncovered Units: "
|
22
|
+
puts uncovered_units.map{ |u| u.to_s.ansi(:yellow) }.sort.join(', ')
|
23
|
+
end
|
24
|
+
|
25
|
+
unless uncovered_cases.empty?
|
26
|
+
puts "\nUncovered Cases: "
|
27
|
+
puts uncovered_cases.map{ |m| m.name.ansi(:red) }.sort.join(', ')
|
28
|
+
end
|
29
|
+
|
30
|
+
puts
|
31
|
+
puts tally
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'lemon/view/cover_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::CoverReports
|
4
|
+
|
5
|
+
class Outline < Abstract
|
6
|
+
|
7
|
+
#
|
8
|
+
def render
|
9
|
+
|
10
|
+
unless covered_units.empty?
|
11
|
+
puts "\nCovered Units:"
|
12
|
+
covered_units.map do |unit|
|
13
|
+
puts "* #{unit.to_s.ansi(:green)}"
|
14
|
+
end.join(", ")
|
15
|
+
end
|
16
|
+
|
17
|
+
unless uncovered_units.empty?
|
18
|
+
puts "\nUncovered Units:"
|
19
|
+
uncovered_units.map do |unit|
|
20
|
+
puts "* #{unit.to_s.ansi(:yellow)}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
unless undefined_units.empty?
|
25
|
+
puts "\nUndefined Units:"
|
26
|
+
unc = undefined_units.map do |unit|
|
27
|
+
puts "* #{unit.to_s.ansi(:red)}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
unless uncovered_cases.empty?
|
32
|
+
puts "\nUncovered Cases:"
|
33
|
+
uncovered_cases.map do |mod|
|
34
|
+
puts "* #{mod.name.ansi(:cyan)}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
puts
|
39
|
+
puts tally
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'lemon/view/cover_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::CoverReports
|
4
|
+
|
5
|
+
class Verbose < Abstract
|
6
|
+
|
7
|
+
#
|
8
|
+
def render
|
9
|
+
|
10
|
+
unless covered_units.empty?
|
11
|
+
puts "\nCovered Cases: "
|
12
|
+
covered_units.map do |unit|
|
13
|
+
puts unit_line(unit, :green)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
unless uncovered_units.empty?
|
18
|
+
puts "\nUncovered Units: "
|
19
|
+
uncovered_units.map do |unit|
|
20
|
+
puts unit_line(unit, :yellow)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
unless undefined_units.empty?
|
25
|
+
puts "\nUndefined Units: "
|
26
|
+
undefined_units.map do |unit|
|
27
|
+
puts unit_line(unit, :red)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
unless uncovered_cases.empty?
|
32
|
+
puts "\nUncovered Cases: "
|
33
|
+
uncovered_cases.map do |mod|
|
34
|
+
puts "* " + mod.name.to_s.ansi(:yellow)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
puts
|
39
|
+
puts tally
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def unit_line(unit, color)
|
44
|
+
data = [unit.to_s.ansi(color), unit.access.to_s, unit.function? ? 'function' : 'instance method']
|
45
|
+
"* %s %s %s" % data
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Lemon::TestReports
|
2
|
+
|
3
|
+
# Test Reporter Base Class
|
4
|
+
class Abstract
|
5
|
+
|
6
|
+
require 'ansi/code'
|
7
|
+
|
8
|
+
#
|
9
|
+
def self.inherited(base)
|
10
|
+
registry << base
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
def self.registry
|
15
|
+
@registry ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
def initialize(runner)
|
20
|
+
@runner = runner
|
21
|
+
@source = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
attr :runner
|
26
|
+
|
27
|
+
#
|
28
|
+
def start_suite(suite)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
def start_case(instance)
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
#def instance(instance)
|
37
|
+
#end
|
38
|
+
|
39
|
+
#
|
40
|
+
def start_unit(unit)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Report an omitted unit test.
|
44
|
+
def omit(unit)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def pass(unit)
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
def fail(unit, exception)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
def error(unit, exception)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
def finish_unit(testunit)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
def finish_case(instance)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
def finish_suite(suite)
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def record ; runner.record ; end
|
74
|
+
|
75
|
+
# Is coverage information requested?
|
76
|
+
#def cover? ; runner.cover? ; end
|
77
|
+
|
78
|
+
#
|
79
|
+
def total
|
80
|
+
%w{pending pass fail error omit}.inject(0){ |s,r| s += record[r.to_sym].size; s }
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
def tally
|
85
|
+
sizes = %w{pending fail error omit pass}.map{ |r| record[r.to_sym].size }
|
86
|
+
data = [total] + sizes
|
87
|
+
s = "%s tests: %s pending, %s fail, %s err, %s omit, %s pass " % data
|
88
|
+
#s += "(#{uncovered_units.size} uncovered, #{undefined_units.size} undefined)" if cover?
|
89
|
+
s
|
90
|
+
end
|
91
|
+
|
92
|
+
#FILE_SEPARATOR = Regexp.escape(File::SEPARATOR)
|
93
|
+
|
94
|
+
#
|
95
|
+
INTERNALS = /(lib|bin)[\\\/]lemon/
|
96
|
+
|
97
|
+
# Clean the backtrace of any reference to ko/ paths and code.
|
98
|
+
def clean_backtrace(backtrace)
|
99
|
+
trace = backtrace.reject{ |bt| bt =~ INTERNALS }
|
100
|
+
trace.map do |bt|
|
101
|
+
if i = bt.index(':in')
|
102
|
+
bt[0...i]
|
103
|
+
else
|
104
|
+
bt
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Have to thank Suraj N. Kurapati for the crux of this code.
|
110
|
+
def code_snippet(exception, bredth=3)
|
111
|
+
backtrace = exception.backtrace.reject{ |bt| bt =~ INTERNALS }
|
112
|
+
backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return ""
|
113
|
+
source_file, source_line = $1, $2.to_i
|
114
|
+
|
115
|
+
source = source(source_file)
|
116
|
+
|
117
|
+
radius = bredth # number of surrounding lines to show
|
118
|
+
region = [source_line - radius, 1].max ..
|
119
|
+
[source_line + radius, source.length].min
|
120
|
+
|
121
|
+
# ensure proper alignment by zero-padding line numbers
|
122
|
+
format = " %2s %0#{region.last.to_s.length}d %s"
|
123
|
+
|
124
|
+
pretty = region.map do |n|
|
125
|
+
format % [('=>' if n == source_line), n, source[n-1].chomp]
|
126
|
+
end #.unshift "[#{region.inspect}] in #{source_file}"
|
127
|
+
|
128
|
+
pretty
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
def source(file)
|
133
|
+
@source[file] ||= (
|
134
|
+
File.readlines(file)
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
# TODO: Show more of the file name than just the basename.
|
139
|
+
def file_and_line(exception)
|
140
|
+
line = exception.backtrace[0]
|
141
|
+
return "" unless line
|
142
|
+
i = line.rindex(':in')
|
143
|
+
line = i ? line[0...i] : line
|
144
|
+
File.basename(line)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'lemon/view/test_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::TestReports
|
4
|
+
|
5
|
+
# Simple Dot-Progress Reporter
|
6
|
+
class Dotprogress < Abstract
|
7
|
+
|
8
|
+
def omit(unit)
|
9
|
+
print "O"; $stdout.flush
|
10
|
+
end
|
11
|
+
|
12
|
+
def pass(unit)
|
13
|
+
print "."; $stdout.flush
|
14
|
+
end
|
15
|
+
|
16
|
+
def fail(unit, exception)
|
17
|
+
print "F"
|
18
|
+
end
|
19
|
+
|
20
|
+
def error(unit, exception)
|
21
|
+
print "E"
|
22
|
+
end
|
23
|
+
|
24
|
+
def pending(unit, exception)
|
25
|
+
print "P"
|
26
|
+
end
|
27
|
+
|
28
|
+
def finish_suite(suite)
|
29
|
+
puts; puts
|
30
|
+
|
31
|
+
## if verbose
|
32
|
+
unless record[:omit].empty?
|
33
|
+
puts "OMITTED:\n\n"
|
34
|
+
puts record[:omit].map{ |u| u.to_s }.sort.join(' ')
|
35
|
+
puts
|
36
|
+
end
|
37
|
+
## end
|
38
|
+
|
39
|
+
unless record[:pending].empty?
|
40
|
+
puts "PENDING:\n\n"
|
41
|
+
record[:pending].each do |testunit, exception|
|
42
|
+
puts " #{testunit}"
|
43
|
+
end
|
44
|
+
puts
|
45
|
+
end
|
46
|
+
|
47
|
+
unless record[:fail].empty?
|
48
|
+
puts "FAILURES:\n\n"
|
49
|
+
record[:fail].each do |testunit, exception|
|
50
|
+
puts " #{testunit}"
|
51
|
+
puts " #{exception}"
|
52
|
+
puts " #{exception.backtrace[0]}"
|
53
|
+
puts
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
unless record[:error].empty?
|
58
|
+
puts "ERRORS:\n\n"
|
59
|
+
record[:error].each do |testunit, exception|
|
60
|
+
puts " #{testunit}"
|
61
|
+
puts " #{exception}"
|
62
|
+
puts " #{exception.backtrace[0]}"
|
63
|
+
puts
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
puts tally
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'lemon/view/test_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::TestReports
|
4
|
+
|
5
|
+
# HTML Test Reporter
|
6
|
+
#
|
7
|
+
# TODO: Make this more like a microformat and add timer info.
|
8
|
+
class Html < Abstract
|
9
|
+
|
10
|
+
#
|
11
|
+
def start_suite(suite)
|
12
|
+
timer_reset
|
13
|
+
@html = []
|
14
|
+
@html << %[<html>]
|
15
|
+
@html << %[<head>]
|
16
|
+
@html << %[<title>Unit Test Report</title>]
|
17
|
+
@html << %[ <style>]
|
18
|
+
@html << %[ html{ background: #fff; margin: 0; padding: 0; font-family: helvetica; }]
|
19
|
+
@html << %[ body{ margin: 0; padding: 0;}]
|
20
|
+
@html << %[ h3{color:#555;}]
|
21
|
+
@html << %[ #main{ margin: 0 auto; color: #110; width: 600px; ]
|
22
|
+
@html << %[ border-right: 1px solid #ddd; border-left: 1px solid #ddd; ]
|
23
|
+
@html << %[ padding: 10px 30px; width: 500px; } ]
|
24
|
+
@html << %[ .lemon{ color: gold; font-size: 22px; font-weight: bold; ]
|
25
|
+
@html << %[ font-family: courier; margin-bottom: -15px;}]
|
26
|
+
@html << %[ .tally{ font-weight: bold; margin-bottom: 10px; }]
|
27
|
+
@html << %[ .omit{ color: cyan; }]
|
28
|
+
@html << %[ .pass{ color: green; }]
|
29
|
+
@html << %[ .fail{ color: red; }]
|
30
|
+
@html << %[ .footer{ font-size: 0.7em; color: #666; margin: 20px 0; }]
|
31
|
+
@html << %[ </style>]
|
32
|
+
@html << %[</head>]
|
33
|
+
@html << %[<body>]
|
34
|
+
@html << %[<div id="main">]
|
35
|
+
@html << %[<div class="lemon">L E M O N</div>]
|
36
|
+
@html << %[<h1>Unit Test Report</h1>]
|
37
|
+
@body = []
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
def start_case(tc)
|
42
|
+
@body << "<h2>"
|
43
|
+
@body << tc.to_s
|
44
|
+
@body << "</h2>"
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def instance(instance)
|
49
|
+
@body << "<h3>"
|
50
|
+
@body << "#{instance}"
|
51
|
+
@body << "</h3>"
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
def start_unit(unit)
|
56
|
+
instance = unit.instance
|
57
|
+
if @instance != instance
|
58
|
+
@instance = instance
|
59
|
+
instance(instance)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
def omit(unit)
|
65
|
+
@body << %[<li class="omit">]
|
66
|
+
@body << " %s %s %s" % ["OMIT", unit.name, unit.aspect]
|
67
|
+
@body << %[</li>]
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
def pass(unit)
|
72
|
+
@body << %[<li class="pass">]
|
73
|
+
@body << "%s %s %s" % ["PASS", unit.name, unit.aspect]
|
74
|
+
@body << %[</li>]
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
def fail(unit, exception)
|
79
|
+
@body << %[<li class="fail">]
|
80
|
+
@body << "%s %s %s" % ["FAIL", unit.name, unit.aspect]
|
81
|
+
@body << "<pre>"
|
82
|
+
@body << " FAIL #{exception.backtrace[0]}"
|
83
|
+
@body << " #{exception}"
|
84
|
+
@body << "</pre>"
|
85
|
+
@body << %[</li>]
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
def error(unit, exception)
|
90
|
+
@body << %[<li class="error">]
|
91
|
+
@body << "%s %s %s" % ["ERROR", unit.name, unit.aspect]
|
92
|
+
@body << "<pre>"
|
93
|
+
@body << " ERROR #{exception.class}"
|
94
|
+
@body << " #{exception}"
|
95
|
+
@body << " " + exception.backtrace.join("\n ")
|
96
|
+
@body << "</pre>"
|
97
|
+
@body << %[</li>]
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
def pending(unit, exception)
|
102
|
+
@body << %[<li class="pending">]
|
103
|
+
@body << " %s %s %s" % ["PENDING", unit.name, unit.aspect]
|
104
|
+
@body << %[</li>]
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
def finish_suite(suite)
|
109
|
+
@html << ""
|
110
|
+
@html << %[<div class="tally">]
|
111
|
+
@html << tally
|
112
|
+
@html << %[</div>]
|
113
|
+
@html << ""
|
114
|
+
|
115
|
+
@body << ""
|
116
|
+
@body << %[<div class="footer">]
|
117
|
+
@body << %[Generated by <a href="http://proutils.github.com/lemon">Lemon</a>]
|
118
|
+
@body << %[on #{Time.now}.]
|
119
|
+
@body << %[</div>]
|
120
|
+
@body << ""
|
121
|
+
@body << %[</div>]
|
122
|
+
@body << %[</div>]
|
123
|
+
@body << ""
|
124
|
+
@body << %[</body>]
|
125
|
+
@body << %[</html>]
|
126
|
+
|
127
|
+
puts @html.join("\n")
|
128
|
+
puts @body.join("\n")
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
def timer
|
133
|
+
secs = Time.now - @time
|
134
|
+
@time = Time.now
|
135
|
+
return "%0.5fs" % [secs.to_s]
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
def timer_reset
|
140
|
+
@time = Time.now
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|