lemon 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.ruby +55 -0
  2. data/APACHE2.txt +206 -0
  3. data/HISTORY.rdoc +12 -0
  4. data/NOTICE.rdoc +16 -0
  5. data/bin/lemon +1 -1
  6. data/demo/case_example_error.rb +10 -0
  7. data/{test/cli → features}/coverage.feature +0 -0
  8. data/{test/cli → features}/generate.feature +0 -0
  9. data/{test/cli → features}/step_definitions/coverage_steps.rb +0 -0
  10. data/{test/cli → features}/support/ae.rb +0 -0
  11. data/{test/cli → features}/support/aruba.rb +0 -0
  12. data/{test/cli → features}/test.feature +0 -0
  13. data/lib/lemon.rb +19 -1
  14. data/lib/lemon.yml +55 -0
  15. data/lib/lemon/cli.rb +0 -1
  16. data/lib/lemon/controller/test_runner.rb +12 -3
  17. data/lib/lemon/model/test_case.rb +4 -2
  18. data/lib/lemon/model/test_unit.rb +45 -2
  19. data/lib/lemon/view/test_reports/abstract.rb +107 -0
  20. data/lib/lemon/view/test_reports/tapj.rb +130 -0
  21. data/lib/lemon/view/test_reports/tapy.rb +141 -0
  22. data/qed/applique/fs.rb +21 -0
  23. data/{test/api/coverage/complete.rdoc → qed/coverage/01_complete.rdoc} +9 -9
  24. data/qed/coverage/02_incomplete.rdoc +97 -0
  25. data/{test/api/coverage/extensions.rdoc → qed/coverage/03_extensions.rdoc} +5 -5
  26. data/test/{unit/case_coverage_analyzer.rb → case_coverage_analyzer.rb} +0 -0
  27. data/test/{unit/case_test_case_dsl.rb → case_test_case_dsl.rb} +0 -0
  28. data/test/fixtures/case_complete.rb +5 -1
  29. data/test/runner +1 -2
  30. metadata +31 -39
  31. data/LICENSE +0 -22
  32. data/lib/lemon/meta/data.rb +0 -29
  33. data/lib/lemon/meta/gemfile +0 -24
  34. data/lib/lemon/meta/profile +0 -17
  35. data/meta/data.rb +0 -29
  36. data/meta/gemfile +0 -24
  37. data/meta/profile +0 -17
  38. data/test/api/applique/fs.rb +0 -18
  39. 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 => @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 => @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} #{instance} .#{target} #{aspect}"
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} #{instance} receiving ##{target} #{aspect}"
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
+