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/TODO ADDED
@@ -0,0 +1,5 @@
1
+ = TODO
2
+
3
+ * Add version or revision field to suite entry.
4
+
5
+
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
- PARSERS = %w{breakdown dotprogress progressbar tap verbose}
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 " + PARSERS.join("\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 = :legacy
52
+ type = :perl
55
53
  when /^\-/
56
- type = :modern
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 :legacy
63
- stream_parser = TAPLegacyParser.new(options)
62
+ when :perl
63
+ stream_parser = PerlParser.new(options)
64
64
  stream_parser.consume(stdin)
65
- else
66
- stream_parser = TAPYParser.new(options)
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
- # NOTE: This is still a work in progress.
9
- #
10
- # TODO: Add support for TAP-J.
8
+ # IMPORTANT: This is still very much a work in progress.
11
9
 
12
- class TAPLegacyAdapter
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'=> count, 'type'=>'header', 'version'=>TAP_Y_VERSION}
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', 'description'=>desc.rstrip}
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
- def convert_not_ok(number, label, metainfo)
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 metainfo
141
- entry['file'] = metainfo['file']
142
- entry['line'] = metainfo['line']
143
- entry['expected'] = metainfo['wanted']
144
- entry['returned'] = metainfo['found']
145
- entry['description'] = metainfo['description']
146
- entry['source'] = metainfo['raw_test']
147
- entry['extra'] = metainfo['extensions']
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['count'] = @count
158
- entry['type'] = 'footer'
159
- entry['tally'] = {
160
- 'pass' => (groups['pass'] || []).size,
161
- 'fail' => (groups['fail'] || []).size
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,4 @@
1
+ require 'tapout/parsers/yaml'
2
+ require 'tapout/parsers/json'
3
+ require 'tapout/parsers/perl'
4
+
@@ -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/tap_legacy_adapter'
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 TAPLegacyParser
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 = TAPLegacyAdapter.new(input)
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 Tap Out report format.
9
- class TAPYParser
7
+ # a TapOut report format.
8
+ class YamlParser
10
9
 
11
10
  #
12
11
  def initialize(options={})
@@ -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/verbose'
4
- require 'tapout/reporters/tap'
4
+ require 'tapout/reporters/html'
5
+ require 'tapout/reporters/outline'
5
6
  require 'tapout/reporters/progressbar'
6
- require 'tapout/reporters/breakdown'
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 'header'
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 'pending', 'skip'
71
+ when 'todo', 'skip', 'pending'
72
72
  skip(entry)
73
73
  end
74
- when 'footer'
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 = entry['count'] || (@passed.size + @failed.size + @raised.size)
131
+ total = @passed.size + @failed.size + @raised.size #+ @skipped.size + @omitted.size
132
132
 
133
- if entry['tally']
134
- count_fail = entry['tally']['fail'] || 0
135
- count_error = entry['tally']['error'] || 0
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['tally']
142
- sums = %w{pass fail error skip}.map{ |e| tally[e] || 0 }
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 pending (%s/%s assertions)"
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 pending"
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)}ko/
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.key, h.value]
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 = [source_line - radius, 1].max ..
216
- [source_line + radius, source.length].min
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
- message = e['message'].strip
101
+ x = e['exception']
102
+ message = x['message'].strip
102
103
  message = message.ansi(:red)
103
104
  puts(message)
104
- puts "#{e['file']}:#{e['line']}"
105
+ puts "#{x['file']}:#{x['line']}"
105
106
  puts
106
- puts code_snippet(e)
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