lemon 0.8.2 → 0.8.3
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/.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
|
+
|