tapout 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby +41 -41
- data/.yardopts +7 -0
- data/COPYING.rdoc +54 -0
- data/HISTORY.rdoc +18 -3
- data/LICENSE.txt +25 -0
- data/PROFILE +33 -0
- data/README.rdoc +20 -29
- data/TAP-YJ.md +346 -0
- data/TODO +5 -0
- data/lib/tapout.rb +15 -12
- data/lib/tapout/{tap_legacy_adapter.rb → adapters/perl.rb} +21 -20
- data/lib/tapout/parsers.rb +4 -0
- data/lib/tapout/parsers/json.rb +38 -0
- data/lib/tapout/{tap_legacy_parser.rb → parsers/perl.rb} +3 -3
- data/lib/tapout/{tapy_parser.rb → parsers/yaml.rb} +2 -3
- data/lib/tapout/reporters.rb +4 -3
- data/lib/tapout/reporters/abstract.rb +21 -18
- data/lib/tapout/reporters/breakdown.rb +4 -3
- data/lib/tapout/reporters/dotprogress.rb +6 -6
- data/lib/tapout/reporters/html.rb +152 -0
- data/lib/tapout/reporters/{verbose.rb → outline.rb} +9 -6
- data/lib/tapout/reporters/progressbar.rb +11 -7
- data/lib/tapout/reporters/tap.rb +15 -8
- data/lib/tapout/version.rb +4 -4
- data/qed/{tap_adapter.rdoc → perl_adapter.rdoc} +3 -3
- metadata +70 -87
- data/APACHE2.txt +0 -204
- data/NOTICE.rdoc +0 -38
- data/TAP-YJ.rdoc +0 -296
data/TODO
ADDED
data/lib/tapout.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
require 'tapout/tapy_parser'
|
2
|
-
require 'tapout/tap_legacy_parser'
|
3
|
-
|
4
1
|
require 'optparse'
|
2
|
+
require 'tapout/parsers'
|
5
3
|
|
6
4
|
module TapOut
|
7
5
|
|
8
|
-
#
|
9
|
-
|
6
|
+
# Usable formats.
|
7
|
+
FORMATS = %w{breakdown dotprogress html outline progressbar tap}
|
10
8
|
|
11
9
|
#
|
12
10
|
def self.cli(*argv)
|
@@ -34,7 +32,7 @@ module TapOut
|
|
34
32
|
$DEBUG = true
|
35
33
|
end
|
36
34
|
|
37
|
-
opt.separator("\nFORMATS:\n " +
|
35
|
+
opt.separator("\nFORMATS:\n " + FORMATS.join("\n "))
|
38
36
|
end
|
39
37
|
|
40
38
|
parser.parse!(argv)
|
@@ -51,19 +49,24 @@ module TapOut
|
|
51
49
|
|
52
50
|
case stdin.line1
|
53
51
|
when /^\d/
|
54
|
-
type = :
|
52
|
+
type = :perl
|
55
53
|
when /^\-/
|
56
|
-
type = :
|
54
|
+
type = :yaml
|
55
|
+
when /^\{/
|
56
|
+
type = :json
|
57
57
|
else
|
58
58
|
raise "Not a recognized TAP stream!"
|
59
59
|
end
|
60
60
|
|
61
61
|
case type
|
62
|
-
when :
|
63
|
-
stream_parser =
|
62
|
+
when :perl
|
63
|
+
stream_parser = PerlParser.new(options)
|
64
64
|
stream_parser.consume(stdin)
|
65
|
-
|
66
|
-
stream_parser =
|
65
|
+
when :yaml
|
66
|
+
stream_parser = YamlParser.new(options)
|
67
|
+
stream_parser.consume(stdin)
|
68
|
+
when :json
|
69
|
+
stream_parser = JsonParser.new(options)
|
67
70
|
stream_parser.consume(stdin)
|
68
71
|
end
|
69
72
|
end
|
@@ -5,11 +5,9 @@ module TapOut
|
|
5
5
|
# The TAP Legacy Adapter transforms traditional TAP format to
|
6
6
|
# modern TAP-Y format.
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# TODO: Add support for TAP-J.
|
8
|
+
# IMPORTANT: This is still very much a work in progress.
|
11
9
|
|
12
|
-
class
|
10
|
+
class PerlAdapter
|
13
11
|
|
14
12
|
#
|
15
13
|
def initialize(input)
|
@@ -89,7 +87,7 @@ module TapOut
|
|
89
87
|
line = cache[0]
|
90
88
|
md = /^(\d+)\.\.(\d+)\s*$/.match(line)
|
91
89
|
count = md[2].to_i - md[1].to_i + 1
|
92
|
-
entry = {'count'=>
|
90
|
+
entry = {'count'=>count, 'type'=>'suite', 'rev'=>REVISION}
|
93
91
|
when :ok
|
94
92
|
line = cache[0]
|
95
93
|
md = /^ok\s+(\d+)\s*\-?\s*(.*?)($|#)/.match(line)
|
@@ -102,7 +100,7 @@ module TapOut
|
|
102
100
|
entry = convert_not_ok(md[1], md[2], data)
|
103
101
|
when :comment
|
104
102
|
desc = cache.map{ |c| c.sub(/^\#\s{0,1}/, '') }.join("\n")
|
105
|
-
entry = {'type'=>'note', '
|
103
|
+
entry = {'type'=>'note', 'text'=>desc.rstrip}
|
106
104
|
end
|
107
105
|
output(entry)
|
108
106
|
@cache = []
|
@@ -131,20 +129,23 @@ module TapOut
|
|
131
129
|
private
|
132
130
|
|
133
131
|
#
|
134
|
-
|
132
|
+
# TODO: Any way to distinguish fail vs error?
|
133
|
+
def convert_not_ok(number, label, data)
|
135
134
|
entry = {}
|
136
135
|
entry['type'] = 'test'
|
137
136
|
entry['status'] = 'fail'
|
138
137
|
entry['index'] = number.to_i
|
139
138
|
entry['label'] = label
|
140
|
-
if
|
141
|
-
entry['
|
142
|
-
entry['
|
143
|
-
entry['
|
144
|
-
entry['
|
145
|
-
|
146
|
-
entry['
|
147
|
-
entry['
|
139
|
+
if data
|
140
|
+
entry['exception'] = {}
|
141
|
+
entry['exception']['file'] = data['file']
|
142
|
+
entry['exception']['line'] = data['line']
|
143
|
+
entry['exception']['message'] = data['description']
|
144
|
+
|
145
|
+
entry['expected'] = data['wanted']
|
146
|
+
entry['returned'] = data['found']
|
147
|
+
entry['source'] = data['raw_test']
|
148
|
+
entry['extra'] = data['extensions']
|
148
149
|
end
|
149
150
|
entry
|
150
151
|
end
|
@@ -154,11 +155,11 @@ module TapOut
|
|
154
155
|
groups = @entries.group_by{ |e| e['status'] }
|
155
156
|
|
156
157
|
entry = {}
|
157
|
-
entry['
|
158
|
-
entry['
|
159
|
-
|
160
|
-
'pass'
|
161
|
-
'fail'
|
158
|
+
entry['type'] = 'tally'
|
159
|
+
entry['counts'] = {
|
160
|
+
'total' => @count,
|
161
|
+
'pass' => (groups['pass'] || []).size,
|
162
|
+
'fail' => (groups['fail'] || []).size
|
162
163
|
}
|
163
164
|
entry
|
164
165
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'tapout/reporters'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module TapOut
|
5
|
+
|
6
|
+
# The TAP-J Parser takes a TAP-J stream and routes it through
|
7
|
+
# a TapOut report format.
|
8
|
+
#
|
9
|
+
class JsonParser
|
10
|
+
|
11
|
+
#
|
12
|
+
def initialize(options={})
|
13
|
+
format = options[:format] || 'dotprogress'
|
14
|
+
@reporter = Reporters.factory(format).new
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
def consume(input)
|
19
|
+
while line = input.gets
|
20
|
+
self << line
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
def <<(line)
|
26
|
+
handle(line)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
def handle(doc)
|
31
|
+
return if doc == ''
|
32
|
+
entry = JSON.load(doc)
|
33
|
+
@reporter << entry
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'tapout/version'
|
2
|
-
require 'tapout/
|
2
|
+
require 'tapout/perl_adapter'
|
3
3
|
require 'tapout/reporters'
|
4
4
|
|
5
5
|
module TapOut
|
6
6
|
|
7
7
|
# The TAPLegacy Parser takes a traditional TAP stream and routes
|
8
8
|
# it through a Tap Out report format.
|
9
|
-
class
|
9
|
+
class perlParser
|
10
10
|
|
11
11
|
# options[:format] - the report format to use
|
12
12
|
def initialize(options={})
|
@@ -16,7 +16,7 @@ module TapOut
|
|
16
16
|
|
17
17
|
# input - any object that responds to #gets
|
18
18
|
def consume(input)
|
19
|
-
parser =
|
19
|
+
parser = PerlAdapter.new(input)
|
20
20
|
parser | @reporter
|
21
21
|
end
|
22
22
|
|
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'tapout/reporters'
|
2
|
-
|
3
2
|
require 'yaml'
|
4
3
|
|
5
4
|
module TapOut
|
6
5
|
|
7
6
|
# The TAP-Y Parser takes a TAP-Y stream and routes it through
|
8
|
-
# a
|
9
|
-
class
|
7
|
+
# a TapOut report format.
|
8
|
+
class YamlParser
|
10
9
|
|
11
10
|
#
|
12
11
|
def initialize(options={})
|
data/lib/tapout/reporters.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'tapout/reporters/abstract'
|
2
|
+
require 'tapout/reporters/breakdown'
|
2
3
|
require 'tapout/reporters/dotprogress'
|
3
|
-
require 'tapout/reporters/
|
4
|
-
require 'tapout/reporters/
|
4
|
+
require 'tapout/reporters/html'
|
5
|
+
require 'tapout/reporters/outline'
|
5
6
|
require 'tapout/reporters/progressbar'
|
6
|
-
require 'tapout/reporters/
|
7
|
+
require 'tapout/reporters/tap'
|
@@ -49,7 +49,7 @@ module TapOut
|
|
49
49
|
# report methods.
|
50
50
|
def handle(entry)
|
51
51
|
case entry['type']
|
52
|
-
when '
|
52
|
+
when 'suite'
|
53
53
|
start_suite(entry)
|
54
54
|
when 'case'
|
55
55
|
finish_case(@previous_case) if @previous_case
|
@@ -68,10 +68,10 @@ module TapOut
|
|
68
68
|
err(entry)
|
69
69
|
when 'omit'
|
70
70
|
omit(entry)
|
71
|
-
when '
|
71
|
+
when 'todo', 'skip', 'pending'
|
72
72
|
skip(entry)
|
73
73
|
end
|
74
|
-
when '
|
74
|
+
when 'tally'
|
75
75
|
finish_case(@previous_case) if @previous_case
|
76
76
|
finish_suite(entry)
|
77
77
|
end
|
@@ -128,30 +128,32 @@ module TapOut
|
|
128
128
|
|
129
129
|
# TODO: get the tally's from the footer entry ?
|
130
130
|
def tally(entry)
|
131
|
-
total =
|
131
|
+
total = @passed.size + @failed.size + @raised.size #+ @skipped.size + @omitted.size
|
132
132
|
|
133
|
-
if entry['
|
134
|
-
|
135
|
-
|
133
|
+
if entry['counts']
|
134
|
+
total = entry['counts']['total'] || total
|
135
|
+
count_fail = entry['counts']['fail'] || 0
|
136
|
+
count_error = entry['counts']['error'] || 0
|
136
137
|
else
|
137
138
|
count_fail = @failed.size
|
138
139
|
count_error = @raised.size
|
139
140
|
end
|
140
141
|
|
141
|
-
if tally = entry['
|
142
|
-
sums = %w{pass fail error
|
142
|
+
if tally = entry['counts']
|
143
|
+
sums = %w{pass fail error todo omit}.map{ |e| tally[e] || 0 }
|
143
144
|
else
|
144
|
-
sums = [@passed, @failed, @raised, @skipped].map{ |e| e.size }
|
145
|
+
sums = [@passed, @failed, @raised, @skipped, @omitted].map{ |e| e.size }
|
145
146
|
end
|
146
147
|
|
148
|
+
# ???
|
147
149
|
assertions = entry['assertions']
|
148
150
|
failures = entry['failures']
|
149
151
|
|
150
152
|
if assertions
|
151
|
-
text = "%s tests: %s pass, %s fail, %s err, %s
|
153
|
+
text = "%s tests: %s pass, %s fail, %s err, %s todo, %omit (%s/%s assertions)"
|
152
154
|
text = text % [total, *sums] + [assertions - failures, assertions]
|
153
155
|
else
|
154
|
-
text = "%s tests: %s pass, %s fail, %s err, %s
|
156
|
+
text = "%s tests: %s pass, %s fail, %s err, %s todo, %s omit"
|
155
157
|
text = text % [total, *sums]
|
156
158
|
end
|
157
159
|
|
@@ -165,7 +167,7 @@ module TapOut
|
|
165
167
|
end
|
166
168
|
|
167
169
|
#
|
168
|
-
INTERNALS = /(lib|bin)#{Regexp.escape(File::SEPARATOR)}
|
170
|
+
INTERNALS = /(lib|bin)#{Regexp.escape(File::SEPARATOR)}tapout/
|
169
171
|
|
170
172
|
# Clean the backtrace of any reference to ko/ paths and code.
|
171
173
|
def clean_backtrace(backtrace)
|
@@ -200,7 +202,7 @@ module TapOut
|
|
200
202
|
end
|
201
203
|
when Array
|
202
204
|
snippet.each do |h|
|
203
|
-
s << [h.
|
205
|
+
s << [h.keys.first, h.values.first]
|
204
206
|
end
|
205
207
|
else
|
206
208
|
##backtrace = exception.backtrace.reject{ |bt| bt =~ INTERNALS }
|
@@ -208,17 +210,18 @@ module TapOut
|
|
208
210
|
#caller =~ /(.+?):(\d+(?=:|\z))/ or return ""
|
209
211
|
#source_file, source_line = $1, $2.to_i
|
210
212
|
|
211
|
-
if File.file?(file)
|
213
|
+
if file && File.file?(file)
|
212
214
|
source = source(file)
|
213
215
|
|
214
216
|
radius = 3 # number of surrounding lines to show
|
215
|
-
region = [
|
216
|
-
[
|
217
|
+
region = [line - radius, 1].max ..
|
218
|
+
[line + radius, source.length].min
|
217
219
|
|
218
220
|
#len = region.last.to_s.length
|
219
221
|
|
220
222
|
s = region.map do |n|
|
221
|
-
format % [n, source[n-1].chomp]
|
223
|
+
#format % [n, source[n-1].chomp]
|
224
|
+
[n, source[n-1].chomp]
|
222
225
|
end
|
223
226
|
end
|
224
227
|
end
|
@@ -98,12 +98,13 @@ module TapOut
|
|
98
98
|
#puts "\n-- Failures and Errors --\n"
|
99
99
|
puts
|
100
100
|
bad.each do |e|
|
101
|
-
|
101
|
+
x = e['exception']
|
102
|
+
message = x['message'].strip
|
102
103
|
message = message.ansi(:red)
|
103
104
|
puts(message)
|
104
|
-
puts "#{
|
105
|
+
puts "#{x['file']}:#{x['line']}"
|
105
106
|
puts
|
106
|
-
puts code_snippet(
|
107
|
+
puts code_snippet(x)
|
107
108
|
end
|
108
109
|
puts
|
109
110
|
end
|
@@ -42,9 +42,9 @@ module TapOut::Reporters
|
|
42
42
|
#backtrace = clean_backtrace(exception.backtrace)
|
43
43
|
$stdout.puts "#{i}. " + (e['label']).ansi(:red)
|
44
44
|
$stdout.puts
|
45
|
-
$stdout.puts " #{e['message']}"
|
46
|
-
$stdout.puts " #{e['file']}:#{e['line']}" #+ backtrace[0]
|
47
|
-
$stdout.puts code_snippet(e)
|
45
|
+
$stdout.puts " #{e['exception']['message']}"
|
46
|
+
$stdout.puts " #{e['exception']['file']}:#{e['exception']['line']}" #+ backtrace[0]
|
47
|
+
$stdout.puts code_snippet(e['exception'])
|
48
48
|
$stdout.puts
|
49
49
|
i += 1
|
50
50
|
end
|
@@ -53,9 +53,9 @@ module TapOut::Reporters
|
|
53
53
|
#backtrace = clean_backtrace(exception.backtrace)
|
54
54
|
$stdout.puts "#{i}. " + (e['label']).ansi(:yellow)
|
55
55
|
$stdout.puts
|
56
|
-
$stdout.puts " #{e['message']}"
|
57
|
-
$stdout.puts " #{e['file']}:#{e['line']}" #+ backtrace[0..2].join(" \n")
|
58
|
-
$stdout.puts code_snippet(e)
|
56
|
+
$stdout.puts " #{e['exception']['message']}"
|
57
|
+
$stdout.puts " #{e['exception']['file']}:#{e['exception']['line']}" #+ backtrace[0..2].join(" \n")
|
58
|
+
$stdout.puts code_snippet(e['exception'])
|
59
59
|
$stdout.puts
|
60
60
|
i += 1
|
61
61
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'test/reporters/abstract'
|
2
|
+
|
3
|
+
module TapOut::Reporters
|
4
|
+
|
5
|
+
# HTML Test Reporter
|
6
|
+
#
|
7
|
+
# This reporter is rather simplistic and rough at this point --in needs
|
8
|
+
# of some TLC.
|
9
|
+
#--
|
10
|
+
# TODO: Make this more like a microformat and add timer info.
|
11
|
+
#++
|
12
|
+
class Html < Abstract
|
13
|
+
|
14
|
+
#
|
15
|
+
def start_suite(entry)
|
16
|
+
timer_reset
|
17
|
+
|
18
|
+
puts %[<html>]
|
19
|
+
puts %[<head>]
|
20
|
+
puts %[<title>Test Report</title>]
|
21
|
+
puts %[ <style>]
|
22
|
+
puts %[ html{ background: #fff; margin: 0; padding: 0; font-family: helvetica; }]
|
23
|
+
puts %[ body{ margin: 0; padding: 0;}]
|
24
|
+
puts %[ h3{color:#555;}]
|
25
|
+
puts %[ #main{ margin: 0 auto; color: #110; width: 600px; ]
|
26
|
+
puts %[ border-right: 1px solid #ddd; border-left: 1px solid #ddd; ]
|
27
|
+
puts %[ padding: 10px 30px; width: 500px; } ]
|
28
|
+
puts %[ .title{ color: gold; font-size: 22px; font-weight: bold; ]
|
29
|
+
puts %[ font-family: courier; margin-bottom: -15px;}]
|
30
|
+
puts %[ .tally{ font-weight: bold; margin-bottom: 10px; }]
|
31
|
+
puts %[ .omit{ color: cyan; }]
|
32
|
+
puts %[ .pass{ color: green; }]
|
33
|
+
puts %[ .fail{ color: red; }]
|
34
|
+
puts %[ .footer{ font-size: 0.7em; color: #666; margin: 20px 0; }]
|
35
|
+
puts %[ </style>]
|
36
|
+
puts %[</head>]
|
37
|
+
puts %[<body>]
|
38
|
+
puts %[<div id="main">]
|
39
|
+
puts %[<div class="title">R U B Y - T E S T</div>]
|
40
|
+
puts %[<h1>Test Report</h1>]
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
def start_case(entry)
|
45
|
+
body = []
|
46
|
+
|
47
|
+
puts "<h2>"
|
48
|
+
puts entry['label']
|
49
|
+
puts "</h2>"
|
50
|
+
|
51
|
+
puts body.join("\n")
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
def start_test(entry)
|
56
|
+
if subtext = entry['subtext']
|
57
|
+
if @subtext != subtext
|
58
|
+
@subtext = subtext
|
59
|
+
puts "<h3>#{subtext}</h3>"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
def pass(entry)
|
66
|
+
puts %[<li class="pass">]
|
67
|
+
puts "%s %s" % [ "PASS", entry['label'] ]
|
68
|
+
puts %[</li>]
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
def fail(entry)
|
73
|
+
e = entry['exception']
|
74
|
+
|
75
|
+
puts %[<li class="fail">]
|
76
|
+
puts "FAIL #{e['message']}"
|
77
|
+
puts "<pre>"
|
78
|
+
puts clean_backtrace(e['backtrace']).join("\n")
|
79
|
+
puts "</pre>"
|
80
|
+
puts %[</li>]
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
def err(entry)
|
85
|
+
e = entry['exception']
|
86
|
+
|
87
|
+
puts %[<li class="error">]
|
88
|
+
puts "ERROR "
|
89
|
+
puts e['message'].to_s
|
90
|
+
puts "<pre>"
|
91
|
+
puts clean_backtrace(e['backtrace']).join("\n")
|
92
|
+
puts "</pre>"
|
93
|
+
puts %[</li>]
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
def todo(entry)
|
98
|
+
e = entry['exception']
|
99
|
+
|
100
|
+
puts %[<li class="pending">]
|
101
|
+
puts "TODO "
|
102
|
+
puts e['message'].to_s
|
103
|
+
puts %[</li>]
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
def omit(entry)
|
108
|
+
e = entry['exception']
|
109
|
+
|
110
|
+
puts %[<li class="omit">]
|
111
|
+
puts "OMIT "
|
112
|
+
puts e['message'].to_s
|
113
|
+
puts %[</li>]
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
def finish_suite(entry)
|
118
|
+
puts ""
|
119
|
+
puts %[<div class="tally">]
|
120
|
+
puts tally(entry)
|
121
|
+
puts %[</div>]
|
122
|
+
puts ""
|
123
|
+
puts ""
|
124
|
+
puts %[<div class="footer">]
|
125
|
+
puts %[Generated by <a href="http://rubyworks.github.com/tapout">TAPOUT</a>]
|
126
|
+
puts %[on #{Time.now}.]
|
127
|
+
puts %[</div>]
|
128
|
+
puts ""
|
129
|
+
puts %[</div>]
|
130
|
+
puts %[</div>]
|
131
|
+
puts ""
|
132
|
+
puts %[</body>]
|
133
|
+
puts %[</html>]
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
#
|
139
|
+
def timer
|
140
|
+
secs = Time.now - @time
|
141
|
+
@time = Time.now
|
142
|
+
return "%0.5fs" % [secs.to_s]
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
def timer_reset
|
147
|
+
@time = Time.now
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|