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