lemon 0.8.2 → 0.8.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +55 -0
- data/APACHE2.txt +206 -0
- data/HISTORY.rdoc +12 -0
- data/NOTICE.rdoc +16 -0
- data/bin/lemon +1 -1
- data/demo/case_example_error.rb +10 -0
- data/{test/cli → features}/coverage.feature +0 -0
- data/{test/cli → features}/generate.feature +0 -0
- data/{test/cli → features}/step_definitions/coverage_steps.rb +0 -0
- data/{test/cli → features}/support/ae.rb +0 -0
- data/{test/cli → features}/support/aruba.rb +0 -0
- data/{test/cli → features}/test.feature +0 -0
- data/lib/lemon.rb +19 -1
- data/lib/lemon.yml +55 -0
- data/lib/lemon/cli.rb +0 -1
- data/lib/lemon/controller/test_runner.rb +12 -3
- data/lib/lemon/model/test_case.rb +4 -2
- data/lib/lemon/model/test_unit.rb +45 -2
- data/lib/lemon/view/test_reports/abstract.rb +107 -0
- data/lib/lemon/view/test_reports/tapj.rb +130 -0
- data/lib/lemon/view/test_reports/tapy.rb +141 -0
- data/qed/applique/fs.rb +21 -0
- data/{test/api/coverage/complete.rdoc → qed/coverage/01_complete.rdoc} +9 -9
- data/qed/coverage/02_incomplete.rdoc +97 -0
- data/{test/api/coverage/extensions.rdoc → qed/coverage/03_extensions.rdoc} +5 -5
- data/test/{unit/case_coverage_analyzer.rb → case_coverage_analyzer.rb} +0 -0
- data/test/{unit/case_test_case_dsl.rb → case_test_case_dsl.rb} +0 -0
- data/test/fixtures/case_complete.rb +5 -1
- data/test/runner +1 -2
- metadata +31 -39
- data/LICENSE +0 -22
- data/lib/lemon/meta/data.rb +0 -29
- data/lib/lemon/meta/gemfile +0 -24
- data/lib/lemon/meta/profile +0 -17
- data/meta/data.rb +0 -29
- data/meta/gemfile +0 -24
- data/meta/profile +0 -17
- data/test/api/applique/fs.rb +0 -18
- data/test/api/coverage/incomplete.rdoc +0 -97
@@ -95,7 +95,8 @@ module Lemon
|
|
95
95
|
@testcase, method,
|
96
96
|
:function => false,
|
97
97
|
:aspect => aspect,
|
98
|
-
:context
|
98
|
+
:context => @context,
|
99
|
+
:caller => caller,
|
99
100
|
&block
|
100
101
|
)
|
101
102
|
#@testcase.steps << unit
|
@@ -114,7 +115,8 @@ module Lemon
|
|
114
115
|
@testcase, method,
|
115
116
|
:function => true,
|
116
117
|
:aspect => aspect,
|
117
|
-
:context
|
118
|
+
:context => @context,
|
119
|
+
:caller => caller,
|
118
120
|
&block
|
119
121
|
)
|
120
122
|
#@testcase.steps << unit
|
@@ -18,6 +18,9 @@ module Lemon
|
|
18
18
|
# Test procedure, in which test assertions should be made.
|
19
19
|
attr :procedure
|
20
20
|
|
21
|
+
#
|
22
|
+
attr :caller
|
23
|
+
|
21
24
|
# New unit test.
|
22
25
|
def initialize(testcase, target, options={}, &procedure)
|
23
26
|
@testcase = testcase
|
@@ -27,6 +30,7 @@ module Lemon
|
|
27
30
|
@function = options[:function] || options[:metaclass]
|
28
31
|
@context = options[:context]
|
29
32
|
@omit = options[:omit]
|
33
|
+
@caller = options[:caller]
|
30
34
|
|
31
35
|
@procedure = procedure
|
32
36
|
|
@@ -87,13 +91,52 @@ module Lemon
|
|
87
91
|
#
|
88
92
|
def description
|
89
93
|
if meta?
|
90
|
-
"#{testcase}
|
94
|
+
#"#{testcase} .#{target} #{aspect}"
|
95
|
+
"#{testcase}.#{target} #{context} #{aspect}".strip
|
91
96
|
else
|
92
97
|
a = /^[aeiou]/i =~ testcase.to_s ? 'An' : 'A'
|
93
|
-
"#{a} #{testcase}
|
98
|
+
#"#{a} #{testcase} receiving ##{target} #{aspect}"
|
99
|
+
"#{testcase}##{target} #{context} #{aspect}".strip
|
94
100
|
end
|
95
101
|
end
|
96
102
|
|
103
|
+
# START-COMMIT. 201105190006
|
104
|
+
|
105
|
+
# The file method returns the file name of +caller+ which
|
106
|
+
# was created upon initialization of this object. It is
|
107
|
+
# also the first element of #file_and_line.
|
108
|
+
#
|
109
|
+
# Returns file name of caller.
|
110
|
+
def file
|
111
|
+
file_and_line.first
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns line number of caller.
|
115
|
+
def line
|
116
|
+
file_and_line.last
|
117
|
+
end
|
118
|
+
|
119
|
+
# The file_and_line method returns the file name and line number of
|
120
|
+
# the caller created upon initialization of this object.
|
121
|
+
#
|
122
|
+
# This method is cached.
|
123
|
+
#
|
124
|
+
# Examples
|
125
|
+
# file_and_line #=> ['foo_test.rb', 123]
|
126
|
+
#
|
127
|
+
# Returns Array of file name and line number of caller.
|
128
|
+
def file_and_line
|
129
|
+
@file_and_line ||= (
|
130
|
+
line = caller[0]
|
131
|
+
i = line.rindex(':in')
|
132
|
+
line = i ? line[0...i] : line
|
133
|
+
f, l = File.basename(line).split(':')
|
134
|
+
[f, l]
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
# END-COMMIT.
|
139
|
+
|
97
140
|
#
|
98
141
|
def match?(match)
|
99
142
|
match == target || match === aspect
|
@@ -128,6 +128,78 @@ module Lemon::TestReports
|
|
128
128
|
pretty
|
129
129
|
end
|
130
130
|
|
131
|
+
=begin
|
132
|
+
#
|
133
|
+
def code_snippet_hash(exception, bredth=3)
|
134
|
+
backtrace = filtered_backtrace(exception)
|
135
|
+
|
136
|
+
backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return ""
|
137
|
+
source_file, source_line = $1, $2.to_i
|
138
|
+
|
139
|
+
source = source(source_file)
|
140
|
+
|
141
|
+
radius = bredth # number of surrounding lines to show
|
142
|
+
region = [source_line - radius, 1].max ..
|
143
|
+
[source_line + radius, source.length].min
|
144
|
+
|
145
|
+
# ensure proper alignment by zero-padding line numbers
|
146
|
+
format = " %2s %0#{region.last.to_s.length}d %s"
|
147
|
+
|
148
|
+
hash = {}
|
149
|
+
region.each do |n|
|
150
|
+
hash[n] = source[n-1].chomp
|
151
|
+
end
|
152
|
+
hash
|
153
|
+
end
|
154
|
+
=end
|
155
|
+
|
156
|
+
#
|
157
|
+
def code_snippet_array(exception, bredth=3)
|
158
|
+
backtrace = filtered_backtrace(exception)
|
159
|
+
backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return ""
|
160
|
+
source_file, source_line = $1, $2.to_i
|
161
|
+
|
162
|
+
source = source(source_file)
|
163
|
+
|
164
|
+
radius = bredth # number of surrounding lines to show
|
165
|
+
region = [source_line - radius, 1].max ..
|
166
|
+
[source_line + radius, source.length].min
|
167
|
+
|
168
|
+
# ensure proper alignment by zero-padding line numbers
|
169
|
+
#format = " %2s %0#{region.last.to_s.length}d %s"
|
170
|
+
|
171
|
+
region.map do |n|
|
172
|
+
source[n-1].chomp
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
def code_snippet_omap(exception, bredth=3)
|
178
|
+
backtrace = filtered_backtrace(exception)
|
179
|
+
backtrace.first =~ /(.+?):(\d+(?=:|\z))/ or return ""
|
180
|
+
source_file, source_line = $1, $2.to_i
|
181
|
+
|
182
|
+
source = source(source_file)
|
183
|
+
|
184
|
+
radius = bredth # number of surrounding lines to show
|
185
|
+
region = [source_line - radius, 1].max ..
|
186
|
+
[source_line + radius, source.length].min
|
187
|
+
|
188
|
+
# ensure proper alignment by zero-padding line numbers
|
189
|
+
#format = " %2s %0#{region.last.to_s.length}d %s"
|
190
|
+
|
191
|
+
a = []
|
192
|
+
region.each do |n|
|
193
|
+
a << {n=> source[n-1].chomp}
|
194
|
+
end
|
195
|
+
a
|
196
|
+
end
|
197
|
+
|
198
|
+
# TODO: improve
|
199
|
+
def code_line(exception)
|
200
|
+
code_snippet_array(exception, 0).first.strip
|
201
|
+
end
|
202
|
+
|
131
203
|
#
|
132
204
|
def source(file)
|
133
205
|
@source[file] ||= (
|
@@ -144,6 +216,41 @@ module Lemon::TestReports
|
|
144
216
|
File.basename(line)
|
145
217
|
end
|
146
218
|
|
219
|
+
#
|
220
|
+
def file_and_line_array(exception)
|
221
|
+
case exception
|
222
|
+
when Exception
|
223
|
+
line = exception.backtrace[0]
|
224
|
+
else
|
225
|
+
line = exception[0] # backtrace
|
226
|
+
end
|
227
|
+
return ["", 0] unless line
|
228
|
+
i = line.rindex(':in')
|
229
|
+
line = i ? line[0...i] : line
|
230
|
+
f, l = File.basename(line).split(':')
|
231
|
+
return [f, l.to_i]
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def file(exception)
|
236
|
+
file_and_line_array(exception).first
|
237
|
+
end
|
238
|
+
|
239
|
+
def line(exception)
|
240
|
+
file_and_line_array(exception).last
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
def filtered_backtrace(exception)
|
245
|
+
case exception
|
246
|
+
when Exception
|
247
|
+
backtrace = exception.backtrace
|
248
|
+
else
|
249
|
+
backtrace = exception
|
250
|
+
end
|
251
|
+
backtrace.reject{ |bt| bt =~ INTERNALS }
|
252
|
+
end
|
253
|
+
|
147
254
|
end
|
148
255
|
|
149
256
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'lemon/view/test_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::TestReports
|
4
|
+
|
5
|
+
# TAP-J Reporter
|
6
|
+
#
|
7
|
+
# TODO: Lemon needs some improvements in order to supply all the
|
8
|
+
# information TAP-J supports. In particular, `file` and `line` information.
|
9
|
+
class Tapj < Abstract
|
10
|
+
|
11
|
+
#
|
12
|
+
def start_suite(suite)
|
13
|
+
require 'json'
|
14
|
+
|
15
|
+
@start = Time.now
|
16
|
+
@i = 0
|
17
|
+
@n = suite.testcases.inject(0){ |c, tc| c = c + tc.size; c }
|
18
|
+
h = {
|
19
|
+
'type' => "header",
|
20
|
+
'count' => @n,
|
21
|
+
'range' => "1..#{@n}"
|
22
|
+
}
|
23
|
+
puts h.to_json
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
def start_case(tcase)
|
28
|
+
h = {
|
29
|
+
'type' => 'case',
|
30
|
+
'description' => "#{tcase.to_s} #{tcase.aspect}".strip
|
31
|
+
}
|
32
|
+
puts h.to_json
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
def start_unit(unit)
|
37
|
+
@i += 1
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
def pass(unit)
|
42
|
+
h = {
|
43
|
+
'type' => 'test',
|
44
|
+
'status' => 'pass',
|
45
|
+
'file' => unit.file,
|
46
|
+
'line' => unit.line,
|
47
|
+
'description' => unit.description,
|
48
|
+
#'returned' => '',
|
49
|
+
#'expected' => '',
|
50
|
+
'source' => code_line(unit.caller),
|
51
|
+
'snippet' => code_snippet_omap(unit.caller, 3),
|
52
|
+
'message' => unit.to_s,
|
53
|
+
'time' => Time.now - @start
|
54
|
+
}
|
55
|
+
puts h.to_json
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
def fail(unit, exception)
|
60
|
+
h = {
|
61
|
+
'type' => 'test',
|
62
|
+
'status' => 'fail',
|
63
|
+
'file' => file(exception),
|
64
|
+
'line' => line(exception),
|
65
|
+
'description' => unit.description,
|
66
|
+
#'returned' => '',
|
67
|
+
#'expected' => '',
|
68
|
+
'source' => code_line(exception),
|
69
|
+
'snippet' => code_snippet_omap(exception, 3),
|
70
|
+
'message' => exception.message,
|
71
|
+
'time' => Time.now - @start
|
72
|
+
#'backtrace' => exception.backtrace
|
73
|
+
}
|
74
|
+
puts h.to_json
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
def error(unit, exception)
|
79
|
+
h = {
|
80
|
+
'type' => 'test',
|
81
|
+
'status' => 'error',
|
82
|
+
'file' => file(exception),
|
83
|
+
'line' => line(exception),
|
84
|
+
'description' => unit.description,
|
85
|
+
'source' => code_line(exception),
|
86
|
+
'snippet' => code_snippet_omap(exception, 3),
|
87
|
+
'message' => exception.message,
|
88
|
+
'trace' => exception.backtrace,
|
89
|
+
'time' => Time.now - @start
|
90
|
+
}
|
91
|
+
puts h.to_json
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO: why was this using expception.backtrace[1] and now [0].
|
95
|
+
def pending(unit, exception)
|
96
|
+
h = {
|
97
|
+
'type' => 'test',
|
98
|
+
'status' => 'pending',
|
99
|
+
'file' => file(exception),
|
100
|
+
'line' => line(exception),
|
101
|
+
'description' => unit.description,
|
102
|
+
'source' => code_line(exception),
|
103
|
+
'snippet' => code_snippet_omap(exception, 3),
|
104
|
+
'message' => exception.message,
|
105
|
+
'time' => Time.now - @start
|
106
|
+
#'backtrace' => exception.backtrace
|
107
|
+
}
|
108
|
+
puts h.to_json
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
def finish_suite(suite)
|
113
|
+
h = {
|
114
|
+
'type' => 'footer',
|
115
|
+
'time' => Time.now - @start,
|
116
|
+
'count' => @n, #total
|
117
|
+
'tally' => {
|
118
|
+
'pass' => record[:pass].size,
|
119
|
+
'fail' => record[:fail].size,
|
120
|
+
'error' => record[:error].size,
|
121
|
+
'omit' => record[:omit].size,
|
122
|
+
'pending' => record[:pending].size # TODO: rename to `hold`?
|
123
|
+
}
|
124
|
+
}
|
125
|
+
puts h.to_json
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'lemon/view/test_reports/abstract'
|
2
|
+
|
3
|
+
module Lemon::TestReports
|
4
|
+
|
5
|
+
#--
|
6
|
+
#returned: true
|
7
|
+
#expected: true
|
8
|
+
#source: ok 1, 2
|
9
|
+
#snippet:
|
10
|
+
# 44: ok 0,0
|
11
|
+
# 45: ok 1,2
|
12
|
+
# 46: ok 2,4
|
13
|
+
#++
|
14
|
+
|
15
|
+
# TAP-Y Reporter
|
16
|
+
#
|
17
|
+
# TODO: Lemon needs some improvements in order to supply all the
|
18
|
+
# information TAP-Y supports. In particular, `file` and `line` information.
|
19
|
+
class Tapy < Abstract
|
20
|
+
|
21
|
+
#
|
22
|
+
def start_suite(suite)
|
23
|
+
require 'yaml'
|
24
|
+
|
25
|
+
@start = Time.now
|
26
|
+
@i = 0
|
27
|
+
@n = suite.testcases.inject(0){ |c, tc| c = c + tc.size; c }
|
28
|
+
h = {
|
29
|
+
'type' => "header",
|
30
|
+
'count' => @n,
|
31
|
+
'range' => "1..#{@n}"
|
32
|
+
}
|
33
|
+
puts h.to_yaml
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
def start_case(tcase)
|
38
|
+
h = {
|
39
|
+
'type' => 'case',
|
40
|
+
'description' => "#{tcase.to_s} #{tcase.aspect}".strip
|
41
|
+
}
|
42
|
+
puts h.to_yaml
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
def start_unit(unit)
|
47
|
+
@i += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
def pass(unit) #, backtrace=nil)
|
52
|
+
h = {
|
53
|
+
'type' => 'test',
|
54
|
+
'status' => 'pass',
|
55
|
+
'file' => unit.file,
|
56
|
+
'line' => unit.line,
|
57
|
+
'description' => unit.description,
|
58
|
+
#'returned' => '',
|
59
|
+
#'expected' => '',
|
60
|
+
'source' => code_line(unit.caller),
|
61
|
+
'snippet' => code_snippet_omap(unit.caller, 3),
|
62
|
+
'message' => unit.to_s,
|
63
|
+
'time' => Time.now - @start
|
64
|
+
}
|
65
|
+
puts h.to_yaml
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
def fail(unit, exception)
|
70
|
+
h = {
|
71
|
+
'type' => 'test',
|
72
|
+
'status' => 'fail',
|
73
|
+
'file' => file(exception),
|
74
|
+
'line' => line(exception),
|
75
|
+
'description' => unit.description,
|
76
|
+
#'returned' => '',
|
77
|
+
#'expected' => '',
|
78
|
+
'source' => code_line(exception),
|
79
|
+
'snippet' => code_snippet_omap(exception, 3),
|
80
|
+
'message' => exception.message,
|
81
|
+
'time' => Time.now - @start
|
82
|
+
#'backtrace' => exception.backtrace
|
83
|
+
}
|
84
|
+
puts h.to_yaml
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
def error(unit, exception)
|
89
|
+
h = {
|
90
|
+
'type' => 'test',
|
91
|
+
'status' => 'error',
|
92
|
+
'file' => file(exception),
|
93
|
+
'line' => line(exception),
|
94
|
+
'description' => unit.description,
|
95
|
+
'source' => code_line(exception),
|
96
|
+
'snippet' => code_snippet_omap(exception, 3),
|
97
|
+
'message' => exception.message,
|
98
|
+
'backtrace' => exception.backtrace,
|
99
|
+
'time' => Time.now - @start
|
100
|
+
}
|
101
|
+
puts h.to_yaml
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
def pending(unit, exception)
|
106
|
+
h = {
|
107
|
+
'type' => 'test',
|
108
|
+
'status' => 'pending',
|
109
|
+
'file' => file(exception),
|
110
|
+
'line' => line(exception),
|
111
|
+
'description' => unit.description,
|
112
|
+
'source' => code_line(exception),
|
113
|
+
'snippet' => code_snippet_omap(exception, 3),
|
114
|
+
'message' => exception.message,
|
115
|
+
'time' => Time.now - @start
|
116
|
+
#'backtrace' => exception.backtrace
|
117
|
+
}
|
118
|
+
puts h.to_yaml
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
def finish_suite(suite)
|
123
|
+
h = {
|
124
|
+
'type' => 'footer',
|
125
|
+
'time' => Time.now - @start,
|
126
|
+
'count' => @n, #total
|
127
|
+
'tally' => {
|
128
|
+
'pass' => record[:pass].size,
|
129
|
+
'fail' => record[:fail].size,
|
130
|
+
'error' => record[:error].size,
|
131
|
+
'omit' => record[:omit].size,
|
132
|
+
'pending' => record[:pending].size # TODO: rename to `hold`?
|
133
|
+
}
|
134
|
+
}
|
135
|
+
puts h.to_yaml
|
136
|
+
puts "..."
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|