test-unit-runner-html 0.0.2
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/History.txt +11 -0
- data/Manifest.txt +6 -0
- data/README.txt +25 -0
- data/Rakefile +24 -0
- data/lib/test/unit/runner/html.rb +46 -0
- data/lib/test/unit/ui/html/testrunner.rb +343 -0
- metadata +61 -0
data/History.txt
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
=== 0.0.1 / 2012-01-12
|
2
|
+
|
3
|
+
* Fixes & Improvements
|
4
|
+
* New config parameters:
|
5
|
+
- report_dir - Where place reports
|
6
|
+
- same-report - Overwrites the same report file if set in true (default),
|
7
|
+
generate new file in each run ({timestamp}_{output_file option}) otherwise.
|
8
|
+
|
9
|
+
=== 0.0.1 / 2011-11-29
|
10
|
+
|
11
|
+
* Initial release
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
= Test::Unit::Runner::HTML
|
2
|
+
|
3
|
+
* http://rubyforge.org/projects/test-unit/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Test::Unit::Runner::HTML - Generates reports in HTML format.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* This provides "--runner=html" option to use HTML UI.
|
12
|
+
|
13
|
+
== INSTALL:
|
14
|
+
|
15
|
+
* sudo gem install test-unit-runner-html
|
16
|
+
|
17
|
+
== USAGE:
|
18
|
+
|
19
|
+
require 'test/unit/runner/html'
|
20
|
+
|
21
|
+
== LICENSE:
|
22
|
+
|
23
|
+
(The Ruby License)
|
24
|
+
|
25
|
+
This software is distributed under the same terms as ruby.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'test-unit'
|
5
|
+
require 'hoe'
|
6
|
+
require './lib/test/unit/runner/html'
|
7
|
+
|
8
|
+
Test::Unit.run = true
|
9
|
+
|
10
|
+
version = Test::Unit::Runner::HTML::VERSION
|
11
|
+
ENV["VERSION"] = version
|
12
|
+
Hoe.new('test-unit-runner-html', version) do |p|
|
13
|
+
p.developer('Ilya Cherepanov', 'ilya.cherepanov@nevosoft.ru')
|
14
|
+
|
15
|
+
p.rubyforge_name = "test-unit"
|
16
|
+
end
|
17
|
+
|
18
|
+
task :tag do
|
19
|
+
message = "Released Test::Unit::Runner::HTML #{version}!"
|
20
|
+
base = "svn+ssh://#{ENV['USER']}@rubyforge.org/var/svn/test-unit/extensions/test-unit-runner-html/"
|
21
|
+
sh 'svn', 'copy', '-m', message, "#{base}trunk", "#{base}tags/#{version}"
|
22
|
+
end
|
23
|
+
|
24
|
+
# vim: syntax=Ruby
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Test
|
2
|
+
module Unit
|
3
|
+
AutoRunner.register_runner(:html) do |auto_runner|
|
4
|
+
require 'test/unit/ui/html/testrunner'
|
5
|
+
Test::Unit::UI::HTML::TestRunner
|
6
|
+
end
|
7
|
+
|
8
|
+
AutoRunner.setup_option do |auto_runner, opts|
|
9
|
+
opts.on("--html-report=FILE", String,
|
10
|
+
"Outputs to file FILE") do |file|
|
11
|
+
auto_runner.runner_options[:output_file] = file
|
12
|
+
end
|
13
|
+
report_options = [
|
14
|
+
["-", false],
|
15
|
+
["no", false],
|
16
|
+
["false", false],
|
17
|
+
["+", true],
|
18
|
+
["yes", true],
|
19
|
+
["true", true],
|
20
|
+
]
|
21
|
+
opts.on("--same-report=FILE", report_options,
|
22
|
+
"Write report in the same file if set, in filename_timestamp.html otherwise.",
|
23
|
+
"(default is false)") do |val|
|
24
|
+
val = false if val.nil?
|
25
|
+
auto_runner.runner_options[:same_report] = val
|
26
|
+
end
|
27
|
+
opts.on("--report-dir=DIR", report_options,
|
28
|
+
"Reports directory") do |val|
|
29
|
+
Dir.mkdir_p val unless Dir.exist? val
|
30
|
+
unless val =~ /[\\\/]$/
|
31
|
+
val =+ File.PATH_SEPARATOR
|
32
|
+
end
|
33
|
+
auto_runner.runner_options[:report_dir] = val
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
module Test
|
39
|
+
module Unit
|
40
|
+
module Assertions
|
41
|
+
def assert_fail msg
|
42
|
+
assert false, build_message(msg)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Ilya Cherepanov
|
4
|
+
# Copyright::
|
5
|
+
# * Copyright (c) 2011 Ilya Cherepanov <ilya.cherepanov@nevosoft.ru>
|
6
|
+
# * Copyright (c) 2011 Kouhei Sutou <kou@clear-code.com>
|
7
|
+
# License:: Ruby license.
|
8
|
+
|
9
|
+
# encode: utf-8
|
10
|
+
|
11
|
+
require 'erb'
|
12
|
+
require 'time'
|
13
|
+
require 'test/unit/ui/testrunner'
|
14
|
+
require 'test/unit/ui/testrunnermediator'
|
15
|
+
|
16
|
+
module Test
|
17
|
+
module Unit
|
18
|
+
module UI
|
19
|
+
module HTML
|
20
|
+
|
21
|
+
# Runs a Test::Unit::TestSuite and outputs HTML.
|
22
|
+
class TestRunner < UI::TestRunner
|
23
|
+
include ERB::Util
|
24
|
+
# Creates a new TestRunner for running the passed
|
25
|
+
# suite. :output option specifies where runner
|
26
|
+
# output should go to; defaults to STDOUT.
|
27
|
+
def initialize(suite, options={})
|
28
|
+
super
|
29
|
+
Dir.mkdir @options[:report_dir] unless Dir.exist? @options[:report_dir]
|
30
|
+
unless @options[:report_dir] =~ /[\\\/]$/
|
31
|
+
@options[:report_dir] = @options[:report_dir] + '/'
|
32
|
+
end
|
33
|
+
dir = @options[:report_dir] || ''
|
34
|
+
of = "#{Time.now.to_i.to_s}_#{@options[:output_file]}" if (@options[:output_file] && !@options[:same_report])
|
35
|
+
of ||= @options[:output_file]
|
36
|
+
@output = File.new(dir+of, "w") if of
|
37
|
+
@output ||= STDOUT
|
38
|
+
@already_outputted = false
|
39
|
+
@indent = 0
|
40
|
+
@top_level = true
|
41
|
+
@current_test = nil
|
42
|
+
@current_test_suite = nil
|
43
|
+
@already_outputted = false
|
44
|
+
@summary = []
|
45
|
+
@cur_suite = nil
|
46
|
+
@cur_suite_cases = []
|
47
|
+
@cur_case = nil
|
48
|
+
@cur_case_tests = []
|
49
|
+
@cur_test = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def attach_to_mediator
|
54
|
+
@mediator.add_listener(TestResult::PASS_ASSERTION,
|
55
|
+
&method(:result_pass_assertion))
|
56
|
+
@mediator.add_listener(TestResult::FAULT,
|
57
|
+
&method(:result_fault))
|
58
|
+
@mediator.add_listener(TestRunnerMediator::STARTED,
|
59
|
+
&method(:started))
|
60
|
+
@mediator.add_listener(TestRunnerMediator::FINISHED,
|
61
|
+
&method(:finished))
|
62
|
+
@mediator.add_listener(TestCase::STARTED_OBJECT,
|
63
|
+
&method(:test_started))
|
64
|
+
@mediator.add_listener(TestCase::FINISHED_OBJECT,
|
65
|
+
&method(:test_finished))
|
66
|
+
@mediator.add_listener(TestSuite::STARTED_OBJECT,
|
67
|
+
&method(:test_suite_started))
|
68
|
+
@mediator.add_listener(TestSuite::FINISHED_OBJECT,
|
69
|
+
&method(:test_suite_finished))
|
70
|
+
end
|
71
|
+
|
72
|
+
def store_test test
|
73
|
+
unless @already_outputted
|
74
|
+
@cur_test.started = test.start_time
|
75
|
+
@cur_test.elapsed = test.elapsed_time
|
76
|
+
@cur_test.passed = test.passed?
|
77
|
+
@cur_test.interrupted = test.interrupted?
|
78
|
+
@cur_test.status = @result.status
|
79
|
+
|
80
|
+
@cur_case_tests << @cur_test
|
81
|
+
@cur_test = []
|
82
|
+
@already_outputted = true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def store_case ca
|
87
|
+
@cur_case_tests.each do |t|
|
88
|
+
@cur_case << t
|
89
|
+
end
|
90
|
+
@cur_case.started = ca.start_time
|
91
|
+
@cur_case.elapsed = ca.elapsed_time
|
92
|
+
@cur_case.passed = ca.passed?
|
93
|
+
@cur_case.status = @result.status
|
94
|
+
@cur_suite_cases << @cur_case
|
95
|
+
@cur_case = []
|
96
|
+
@cur_case_tests = []
|
97
|
+
end
|
98
|
+
|
99
|
+
def store_suite suite
|
100
|
+
@cur_suite_cases.each do |c|
|
101
|
+
@cur_suite << c
|
102
|
+
end
|
103
|
+
@cur_suite.started = suite.start_time
|
104
|
+
@cur_suite.elapsed = suite.elapsed_time
|
105
|
+
@cur_suite.passed = suite.passed?
|
106
|
+
@cur_suite.status = @result.status
|
107
|
+
@cur_suite.result = @result
|
108
|
+
@summary << @cur_suite
|
109
|
+
@cur_suite_cases = []
|
110
|
+
@cur_suite = []
|
111
|
+
end
|
112
|
+
|
113
|
+
def started(result)
|
114
|
+
@result = result
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_suite_started(suite)
|
118
|
+
@current_test_suite = suite
|
119
|
+
|
120
|
+
if suite.test_case.nil?
|
121
|
+
@cur_suite = ResultSuite.new suite.name
|
122
|
+
else
|
123
|
+
@cur_case = ResultCase.new suite.name
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_started(test)
|
128
|
+
@already_outputted = false
|
129
|
+
@current_test = test
|
130
|
+
@cur_test = ResultTest.new test.name
|
131
|
+
end
|
132
|
+
|
133
|
+
def result_pass_assertion(result)
|
134
|
+
@cur_test.assertion
|
135
|
+
end
|
136
|
+
|
137
|
+
def result_fault(fault)
|
138
|
+
@cur_test << fault
|
139
|
+
store_test @current_test
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_finished(test)
|
143
|
+
store_test test
|
144
|
+
@current_test = nil
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_suite_finished(suite)
|
148
|
+
if suite.test_case.nil?
|
149
|
+
store_suite suite
|
150
|
+
else
|
151
|
+
store_case suite
|
152
|
+
end
|
153
|
+
@current_test_suite = nil
|
154
|
+
end
|
155
|
+
|
156
|
+
def finished(elapsed_time)
|
157
|
+
if $DEBUG
|
158
|
+
o = File.new 'debug.log', 'w'
|
159
|
+
o.puts YAML.dump @summary
|
160
|
+
end
|
161
|
+
head
|
162
|
+
@summary.each do |suite|
|
163
|
+
output_suite suite
|
164
|
+
end
|
165
|
+
footer
|
166
|
+
end
|
167
|
+
|
168
|
+
def output_suite suite
|
169
|
+
open_tag 'article', 'box suite rounded '+suite.status do
|
170
|
+
content '', 'div', 'light rounded'
|
171
|
+
content suite.name, 'div', 'name'
|
172
|
+
open_tag 'div', 'stats' do
|
173
|
+
content 'Started: '+suite.started.ctime.to_s, 'div', 'time'
|
174
|
+
content 'Elapsed: '+suite.elapsed.to_s, 'div', 'time'
|
175
|
+
content "#{suite.n_test} tests in #{suite.n_case} cases", 'div', 'num-info'
|
176
|
+
end
|
177
|
+
button suite.passed
|
178
|
+
open_tag 'section', 'more' do
|
179
|
+
suite.cases.each do |ca|
|
180
|
+
output_case ca
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def output_case ca
|
187
|
+
open_tag 'article', 'box rounded case '+ca.status do
|
188
|
+
content '', 'div', 'light rounded'
|
189
|
+
content ca.name, 'div', 'name'
|
190
|
+
open_tag 'div', 'stats' do
|
191
|
+
content 'Started: '+ca.started.ctime.to_s, 'div', 'time'
|
192
|
+
content 'Elapsed: '+ca.elapsed.to_s, 'div', 'time'
|
193
|
+
content "#{ca.n_test} tests", 'div', 'num-info'
|
194
|
+
end
|
195
|
+
button ca.passed
|
196
|
+
open_tag 'section', 'more' do
|
197
|
+
ca.tests.each do |t|
|
198
|
+
output_test t
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def output_test test
|
205
|
+
open_tag 'article', 'box rounded test '+test.status do
|
206
|
+
content '', 'div', 'light rounded'
|
207
|
+
content test.name, 'div', 'name'
|
208
|
+
open_tag 'div', 'stats' do
|
209
|
+
content 'Started: '+test.started.ctime.to_s, 'div', 'time'
|
210
|
+
content 'Elapsed: '+test.elapsed.to_s, 'div', 'time'
|
211
|
+
end
|
212
|
+
if !test.passed && !test.faults.nil? && !test.faults.empty?
|
213
|
+
button test.passed
|
214
|
+
open_tag 'section', 'more_test' do
|
215
|
+
test.faults.each do |f|
|
216
|
+
open_tag 'div', 'box error' do
|
217
|
+
file, line = f.location[0].scan( /(.*):(?!\/)(\d+)/)[0]
|
218
|
+
content f.message, 'div', 'message'
|
219
|
+
content 'in '+(File.basename file), 'div', 'file'
|
220
|
+
content 'on line # '+line.to_s, 'div', 'line'
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def head
|
229
|
+
@output.puts "<!DOCTYPE html>
|
230
|
+
<html>
|
231
|
+
<head>
|
232
|
+
<meta charset='utf-8'>
|
233
|
+
<title>Test report created at #{Time.now.ctime}</title>
|
234
|
+
<link rel='stylesheet' type='text/css' href='css/main.css' />
|
235
|
+
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js'></script>
|
236
|
+
<script type='text/javascript' src='js/main.js'></script>
|
237
|
+
</head>
|
238
|
+
<body>
|
239
|
+
<section class='container'>"
|
240
|
+
end
|
241
|
+
|
242
|
+
def footer
|
243
|
+
@output.puts " </section>
|
244
|
+
<footer>
|
245
|
+
<a href='#'>Link</a>
|
246
|
+
</footer>
|
247
|
+
</body>\n</html>"
|
248
|
+
end
|
249
|
+
|
250
|
+
def button passed
|
251
|
+
content passed ? '+' : '-', 'div', 'expand button'
|
252
|
+
end
|
253
|
+
def indent
|
254
|
+
" " * @indent
|
255
|
+
end
|
256
|
+
|
257
|
+
def open_tag(tag, classname=nil)
|
258
|
+
@output.puts "#{indent}<#{tag}#{classname.nil? ? '' : ' class="'+classname+'"'}>"
|
259
|
+
@indent += 2
|
260
|
+
if block_given?
|
261
|
+
yield
|
262
|
+
close_tag(tag)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def content(cont, tag=nil, classname=nil)
|
267
|
+
return if cont.nil?
|
268
|
+
case cont
|
269
|
+
when Time
|
270
|
+
cont = cont.ctime
|
271
|
+
end
|
272
|
+
if tag.nil?
|
273
|
+
@output.puts "#{indent}#{h(cont)}"
|
274
|
+
elsif
|
275
|
+
@output.puts "#{indent}<#{tag}#{classname.nil? ? '' : ' class="'+classname+'"'}>#{h(cont)}</#{tag}>"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def close_tag(tag)
|
280
|
+
@indent -= 2
|
281
|
+
@output.puts("#{indent}</#{tag}>")
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
class Result
|
291
|
+
attr_accessor :name, :status, :result
|
292
|
+
attr_accessor :started, :elapsed, :passed
|
293
|
+
def initialize n
|
294
|
+
@name = n if n =~ /^test/
|
295
|
+
@name ||= File.basename n, '.rb'
|
296
|
+
@status = ''
|
297
|
+
@result = ''
|
298
|
+
end
|
299
|
+
def name= n
|
300
|
+
@name = nil
|
301
|
+
@name = n if n =~ /^test/
|
302
|
+
@name ||= File.basename n, '.rb'
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class ResultSuite < Result
|
307
|
+
attr_accessor :cases, :n_case, :n_test
|
308
|
+
|
309
|
+
def << (suite_case)
|
310
|
+
@cases ||= []
|
311
|
+
@cases.push suite_case
|
312
|
+
@n_case ||= 0
|
313
|
+
@n_case += 1
|
314
|
+
@n_test ||= 0
|
315
|
+
@n_test += suite_case.n_test
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
class ResultCase < Result
|
320
|
+
attr_accessor :tests, :n_test
|
321
|
+
|
322
|
+
def << (test)
|
323
|
+
@tests ||= []
|
324
|
+
@tests.push test
|
325
|
+
@n_test ||= 0
|
326
|
+
@n_test += 1
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
class ResultTest < Result
|
331
|
+
attr_accessor :assertions, :faults, :interrupted
|
332
|
+
|
333
|
+
def << (fault)
|
334
|
+
@faults ||= []
|
335
|
+
@faults.push fault
|
336
|
+
end
|
337
|
+
|
338
|
+
def assertion
|
339
|
+
@assertions ||= 0
|
340
|
+
@assertions += 1
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: test-unit-runner-html
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Cherepanov Ilya
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: test-unit
|
16
|
+
requirement: &19148484 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.4.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *19148484
|
25
|
+
description: A Ruby library to generate Test::Unit reports in HTML format
|
26
|
+
email: ilya.cherepanov@nevosoft.ru
|
27
|
+
executables: []
|
28
|
+
extensions: []
|
29
|
+
extra_rdoc_files: []
|
30
|
+
files:
|
31
|
+
- History.txt
|
32
|
+
- README.txt
|
33
|
+
- Rakefile
|
34
|
+
- Manifest.txt
|
35
|
+
- lib/test/unit/runner/html.rb
|
36
|
+
- lib/test/unit/ui/html/testrunner.rb
|
37
|
+
homepage: http://nevosoft.ru
|
38
|
+
licenses: []
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.3.6
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project: test-unit-runner-html
|
57
|
+
rubygems_version: 1.8.11
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: Generates Test::Unit reports in HTML format
|
61
|
+
test_files: []
|