lineman 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +82 -0
- data/lib/test/unit/runner/statistics.rb +21 -0
- data/lib/test/unit/ui/statistics/base_runner.rb +146 -0
- data/lib/test/unit/ui/statistics/runners/lineman.rb +109 -0
- data/lib/test/unit/ui/statistics.rb +65 -0
- data/lineman.gemspec +22 -0
- metadata +65 -0
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
`lineman` is a test runner for
|
2
|
+
[test-unit](https://github.com/test-unit/test-unit) that prints a line per test,
|
3
|
+
with statistics about its performance.
|
4
|
+
|
5
|
+
Example output:
|
6
|
+
|
7
|
+
NPCRobotTest
|
8
|
+
test: should have a cool nickname 0:1 0.01 N
|
9
|
+
test: should have big birthday party 1:1 0.00
|
10
|
+
test: should have pizzas for party 0:0 0.00 ON
|
11
|
+
test: should not dance 0:0 0.00 P
|
12
|
+
NPCRobotTest 1:2 0.01 PON
|
13
|
+
test/npc_robot_test 1:2 0.01 PON
|
14
|
+
|
15
|
+
========
|
16
|
+
Failures
|
17
|
+
========
|
18
|
+
|
19
|
+
1) Failure:
|
20
|
+
test: should have a cool nickname(NPCRobotTest) [test/npc_robot_test.rb:30]:
|
21
|
+
<"claptrap"> expected but was
|
22
|
+
<"Claptrap">.
|
23
|
+
|
24
|
+
diff:
|
25
|
+
- claptrap
|
26
|
+
? ^
|
27
|
+
+ Claptrap
|
28
|
+
? ^
|
29
|
+
|
30
|
+
2) Failure:
|
31
|
+
test: should have big birthday party(NPCRobotTest) [test/npc_robot_test.rb:14]:
|
32
|
+
Pending block should not be passed: acquaintances != friends.
|
33
|
+
|
34
|
+
|
35
|
+
=========
|
36
|
+
Omissions
|
37
|
+
=========
|
38
|
+
|
39
|
+
1) Omission: I am not sure what a pizza is
|
40
|
+
test: should have pizzas for party(NPCRobotTest)
|
41
|
+
test/npc_robot_test.rb:43:in `block in <class:NPCRobotTest>'
|
42
|
+
|
43
|
+
|
44
|
+
========
|
45
|
+
Pendings
|
46
|
+
========
|
47
|
+
|
48
|
+
1) Pending: pended.
|
49
|
+
test: should not dance(NPCRobotTest)
|
50
|
+
test/npc_robot_test.rb:23:in `block in <class:NPCRobotTest>'
|
51
|
+
|
52
|
+
|
53
|
+
=============
|
54
|
+
Notifications
|
55
|
+
=============
|
56
|
+
|
57
|
+
1) Notification: Claptrap
|
58
|
+
test: should have a cool nickname(NPCRobotTest)
|
59
|
+
test/npc_robot_test.rb:29:in `block in <class:NPCRobotTest>'
|
60
|
+
|
61
|
+
2) Notification: NPCRobotTest#test: should have pizzas for party was redefined
|
62
|
+
test: should have pizzas for party(NPCRobotTest)
|
63
|
+
test/npc_robot_test.rb:42:in `<class:NPCRobotTest>'
|
64
|
+
test/npc_robot_test.rb:10:in `<main>'
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
Finished in 0.013893
|
69
|
+
4 tests, 3 assertions, 2 failures, 0 errors, 1 pendings, 1 omissions, 2 notifications
|
70
|
+
|
71
|
+
The characters P, O, and N that are reported in the last column map to calls to
|
72
|
+
`pend`, `omit`, and `notify` inside of the test. (These are part of test-unit).
|
73
|
+
|
74
|
+
Usage:
|
75
|
+
|
76
|
+
* put lineman in you bundle or otherwise install the gem
|
77
|
+
* `Test::Unit::AutoRunner.default_runner = 'lineman'`
|
78
|
+
* You could also specify --runner on the command line or your test-unit.yml
|
79
|
+
* Run your tests!
|
80
|
+
|
81
|
+
Make pull requests and be friendly.
|
82
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test/unit/ui/statistics/base_runner'
|
2
|
+
|
3
|
+
|
4
|
+
module Test::Unit # :nodoc:
|
5
|
+
class << self
|
6
|
+
def require_statistics_runners # :nodoc:
|
7
|
+
stat_dir = File.expand_path('../ui/statistics/runners',
|
8
|
+
File.dirname(__FILE__))
|
9
|
+
Dir["#{stat_dir}/*.rb"].each do |f|
|
10
|
+
require_relative "../ui/statistics/runners/#{File.basename(f)}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require_statistics_runners
|
16
|
+
Test::Unit::UI::Statistics::BaseRunner.each_subclass do |subclass|
|
17
|
+
AutoRunner.register_runner(subclass.name) do |_auto_runner|
|
18
|
+
subclass
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'test/unit/ui/testrunner'
|
2
|
+
require 'test/unit/ui/testrunnermediator'
|
3
|
+
require_relative '../statistics'
|
4
|
+
|
5
|
+
module Test::Unit::UI::Statistics
|
6
|
+
|
7
|
+
# Inherit from this class to create a runner that has access to statistics.
|
8
|
+
#
|
9
|
+
# Implement any/all of the following callbacks in your subclass:
|
10
|
+
# * reset
|
11
|
+
# * started
|
12
|
+
# * finished
|
13
|
+
# * suite_started
|
14
|
+
# * suite_finished
|
15
|
+
# * case_started
|
16
|
+
# * case_finished
|
17
|
+
# * assertion
|
18
|
+
# * error
|
19
|
+
# * failure
|
20
|
+
# * pending
|
21
|
+
# * omission
|
22
|
+
# * notification
|
23
|
+
# * fault
|
24
|
+
#
|
25
|
+
class BaseRunner < Test::Unit::UI::TestRunner # :nodoc:
|
26
|
+
|
27
|
+
attr_accessor :result
|
28
|
+
|
29
|
+
# Case Statistics
|
30
|
+
def cstat
|
31
|
+
@method_metrics
|
32
|
+
end
|
33
|
+
|
34
|
+
# Suite Statistics
|
35
|
+
def sstat
|
36
|
+
@suite_stack.last
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.inherited kls #:nodoc:
|
40
|
+
(@@subclasses ||= []) << kls
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.each_subclass #:nodoc:
|
44
|
+
@@subclasses.each {|kls| yield kls }
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize suite, options={} #:nodoc:
|
48
|
+
super
|
49
|
+
@suite_stack = []
|
50
|
+
@output = options[:output] || STDOUT
|
51
|
+
end
|
52
|
+
|
53
|
+
# Inherited from TestRunner, sets up callbacks via i-var.
|
54
|
+
def attach_to_mediator #:nodoc:
|
55
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::RESET,
|
56
|
+
&callback(:reset))
|
57
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::STARTED,
|
58
|
+
&callback(:stats_started, :started))
|
59
|
+
@mediator.add_listener(Test::Unit::UI::TestRunnerMediator::FINISHED,
|
60
|
+
&callback(:finished))
|
61
|
+
@mediator.add_listener(Test::Unit::TestSuite::STARTED_OBJECT,
|
62
|
+
&callback(:stats_suite_started, :suite_started))
|
63
|
+
@mediator.add_listener(Test::Unit::TestSuite::FINISHED_OBJECT,
|
64
|
+
&callback(:suite_finished, :stats_suite_finished))
|
65
|
+
@mediator.add_listener(Test::Unit::TestCase::STARTED,
|
66
|
+
&callback(:stats_case_started, :case_started))
|
67
|
+
@mediator.add_listener(Test::Unit::TestCase::FINISHED,
|
68
|
+
&callback(:stats_case_finished, :case_finished))
|
69
|
+
@mediator.add_listener(Test::Unit::TestResult::PASS_ASSERTION,
|
70
|
+
&callback(:stats_assertion, :assertion))
|
71
|
+
@mediator.add_listener(Test::Unit::TestResult::FAULT,
|
72
|
+
&callback(:stats_fault, :fault))
|
73
|
+
end
|
74
|
+
|
75
|
+
# Forward to methods (in-order) that we might respond to.
|
76
|
+
def callback *types #:nodoc:
|
77
|
+
->(*args) { types.select{|t| respond_to?(t) }.each{|t| send(t, *args) }}
|
78
|
+
end
|
79
|
+
|
80
|
+
def stats_started result #:nodoc:
|
81
|
+
@result = result
|
82
|
+
end
|
83
|
+
|
84
|
+
def stats_suite_started testsuite #:nodoc:
|
85
|
+
@suite_stack << SuiteStatistics.new(testsuite)
|
86
|
+
end
|
87
|
+
|
88
|
+
def stats_suite_finished testsuite #:nodoc:
|
89
|
+
@suite_stack.pop
|
90
|
+
end
|
91
|
+
|
92
|
+
def stats_case_started name #:nodoc:
|
93
|
+
@method_metrics = CaseStatistics.new(@result)
|
94
|
+
end
|
95
|
+
|
96
|
+
def stats_case_finished name #:nodoc:
|
97
|
+
@method_metrics.complete
|
98
|
+
update_suites_with_case_statistics
|
99
|
+
end
|
100
|
+
|
101
|
+
def stats_assertion testresult #:nodoc:
|
102
|
+
@method_metrics.pass += 1
|
103
|
+
end
|
104
|
+
|
105
|
+
def stats_fault fault #:nodoc:
|
106
|
+
base_type = fault.class.name.split('::').last.downcase
|
107
|
+
stat_method_name = "stats_handle_#{base_type}".to_sym
|
108
|
+
method_name = "#{base_type}".to_sym
|
109
|
+
send(stat_method_name, fault)
|
110
|
+
if respond_to?(method_name)
|
111
|
+
send(method_name, fault)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def stats_handle_error fault #:nodoc:
|
116
|
+
# Don't count errors before setup (i.e. in startup).
|
117
|
+
return unless @method_metrics
|
118
|
+
@method_metrics.errs += 1
|
119
|
+
end
|
120
|
+
|
121
|
+
def stats_handle_failure fault #:nodoc:
|
122
|
+
# The PASS_ASSERTION message just means it was run, we handle failures
|
123
|
+
@method_metrics.fail += 1
|
124
|
+
@method_metrics.pass -= 1
|
125
|
+
end
|
126
|
+
|
127
|
+
def stats_handle_pending fault #:nodoc:
|
128
|
+
@method_metrics.pend += 1
|
129
|
+
end
|
130
|
+
|
131
|
+
def stats_handle_omission fault #:nodoc:
|
132
|
+
@method_metrics.omit += 1
|
133
|
+
end
|
134
|
+
|
135
|
+
def stats_handle_notification fault #:nodoc:
|
136
|
+
@method_metrics.note += 1
|
137
|
+
end
|
138
|
+
|
139
|
+
def update_suites_with_case_statistics #:nodoc:
|
140
|
+
@suite_stack.each do |suite|
|
141
|
+
suite.add_test_case_statistics @method_metrics
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require_relative '../base_runner'
|
2
|
+
#require 'test/unit/color-scheme'
|
3
|
+
#require 'test/unit/code-snippet-fetcher'
|
4
|
+
#require 'test/unit/diff'
|
5
|
+
|
6
|
+
|
7
|
+
module Test::Unit::UI::Statistics::Lineman
|
8
|
+
class TestRunner < Test::Unit::UI::Statistics::BaseRunner
|
9
|
+
|
10
|
+
def self.name
|
11
|
+
'lineman'
|
12
|
+
end
|
13
|
+
|
14
|
+
# callback
|
15
|
+
def reset size
|
16
|
+
puts "Preparing to run #{size} tests"
|
17
|
+
end
|
18
|
+
|
19
|
+
# callback
|
20
|
+
def finished time
|
21
|
+
show_faults
|
22
|
+
puts "Finished in #{time.inspect}"
|
23
|
+
puts result.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
# callback
|
27
|
+
def suite_started testcase
|
28
|
+
puts "%-0.80s" % testcase
|
29
|
+
end
|
30
|
+
|
31
|
+
# callback
|
32
|
+
def suite_finished testcase
|
33
|
+
two_point_time = ("%.2f" % sstat.elapsed_time)
|
34
|
+
extended_actions = ''
|
35
|
+
extended_actions += (sstat.errs.nonzero? ? 'E' : ' ')
|
36
|
+
extended_actions += (sstat.pend.nonzero? ? 'P' : ' ')
|
37
|
+
extended_actions += (sstat.omit.nonzero? ? 'O' : ' ')
|
38
|
+
extended_actions += (sstat.note.nonzero? ? 'N' : ' ')
|
39
|
+
puts "%-50.50s %4.4s:%-4.4s %6.6s %s" % [testcase,
|
40
|
+
sstat.pass,
|
41
|
+
sstat.fail,
|
42
|
+
two_point_time,
|
43
|
+
extended_actions]
|
44
|
+
end
|
45
|
+
|
46
|
+
# callback
|
47
|
+
def case_started test_name
|
48
|
+
output_name = test_name.gsub(/\(#{sstat.test_suite.to_s}\)$/, '')
|
49
|
+
print " %-46.46s " % output_name
|
50
|
+
end
|
51
|
+
|
52
|
+
# callback
|
53
|
+
def case_finished test_name
|
54
|
+
two_point_time = ("%.2f" % cstat.elapsed_time)
|
55
|
+
if cstat.errs.nonzero?
|
56
|
+
err_or_pass_fail = 'ERROR'
|
57
|
+
else
|
58
|
+
err_or_pass_fail = '%4.4s:%-4.4s' % [cstat.pass,
|
59
|
+
cstat.fail]
|
60
|
+
end
|
61
|
+
extended_actions = ''
|
62
|
+
extended_actions += (cstat.pend.nonzero? ? 'P' : ' ')
|
63
|
+
extended_actions += (cstat.omit.nonzero? ? 'O' : ' ')
|
64
|
+
extended_actions += (cstat.note.nonzero? ? 'N' : ' ')
|
65
|
+
puts "%9.9s %6.6s %s" % [err_or_pass_fail, two_point_time,
|
66
|
+
extended_actions]
|
67
|
+
end
|
68
|
+
|
69
|
+
def show_faults
|
70
|
+
puts "\n"
|
71
|
+
order = [Test::Unit::Error, Test::Unit::Failure, Test::Unit::Omission,
|
72
|
+
Test::Unit::Pending, Test::Unit::Notification]
|
73
|
+
order.each do |type|
|
74
|
+
faults = result.faults.select {|f| f.is_a?(type) }
|
75
|
+
if faults.length.nonzero?
|
76
|
+
type_str = "#{type.to_s.split('::').last}s"
|
77
|
+
puts "=" * type_str.length
|
78
|
+
puts type_str
|
79
|
+
puts "=" * type_str.length
|
80
|
+
puts ''
|
81
|
+
max_width = faults.length.to_s.length
|
82
|
+
faults.each_with_index do |f,i|
|
83
|
+
puts " %#{max_width}i) %s\n" % [i+1, f.long_display]
|
84
|
+
puts ''
|
85
|
+
end
|
86
|
+
puts ''
|
87
|
+
end
|
88
|
+
end
|
89
|
+
puts "\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
def fault_header_line num, fault
|
93
|
+
" %#{@exception_max_width}i) %s:" % [num, fault.label]
|
94
|
+
end
|
95
|
+
|
96
|
+
def fault_location_line fault
|
97
|
+
"#{fault.test_name} #{fault.location}:"
|
98
|
+
end
|
99
|
+
|
100
|
+
def fault_message fault
|
101
|
+
fault.message
|
102
|
+
end
|
103
|
+
|
104
|
+
def exception_backtrace fault
|
105
|
+
" " + fault.backtrace.join("\n ")
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Test::Unit::UI::Statistics
|
2
|
+
class CaseStatistics
|
3
|
+
attr_accessor :pass
|
4
|
+
attr_accessor :fail
|
5
|
+
attr_accessor :errs
|
6
|
+
attr_accessor :pend
|
7
|
+
attr_accessor :omit
|
8
|
+
attr_accessor :note
|
9
|
+
|
10
|
+
def initialize results
|
11
|
+
self.pass = 0
|
12
|
+
self.fail = 0
|
13
|
+
self.errs = 0
|
14
|
+
self.pend = 0
|
15
|
+
self.omit = 0
|
16
|
+
self.note = 0
|
17
|
+
@start_time = Time.now
|
18
|
+
@end_time = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def elapsed_time
|
22
|
+
end_time - @start_time
|
23
|
+
end
|
24
|
+
|
25
|
+
def end_time
|
26
|
+
@end_time ? @end_time : Time.now
|
27
|
+
end
|
28
|
+
|
29
|
+
def complete
|
30
|
+
@end_time = Time.now
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class SuiteStatistics
|
36
|
+
attr_reader :test_suite
|
37
|
+
attr_accessor :forward_class
|
38
|
+
|
39
|
+
def initialize test_suite
|
40
|
+
@test_suite = test_suite
|
41
|
+
@aggregate_statistics = []
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_test_case_statistics stats
|
45
|
+
@aggregate_statistics << stats
|
46
|
+
end
|
47
|
+
|
48
|
+
def aggregate sym
|
49
|
+
@aggregate_statistics.inject(0) {|a,v| a + v.send(sym) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing sym, *args
|
53
|
+
if respond_to?(sym)
|
54
|
+
aggregate(sym)
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_to? sym
|
61
|
+
Test::Unit::TestResult.instance_methods.include?(sym) or
|
62
|
+
CaseStatistics.instance_methods.include?(sym)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lineman.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'lineman'
|
3
|
+
s.version = '0.9.0'
|
4
|
+
s.date = '2012-10-07'
|
5
|
+
|
6
|
+
s.summary = 'A test runner (UI) for test-unit'
|
7
|
+
s.description = 'Lineman provides clean output for your tests, giving you one line for each test that shows the name along with some statistics.'
|
8
|
+
|
9
|
+
s.authors = ['Todd Willey (xtoddx)']
|
10
|
+
s.email = 'xtoddx@gmail.com'
|
11
|
+
s.homepage = 'http://github.com/xtoddx/lineman'
|
12
|
+
|
13
|
+
s.require_paths = %w[lib]
|
14
|
+
|
15
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
16
|
+
s.extra_rdoc_files = %w[README.md]
|
17
|
+
|
18
|
+
s.add_dependency('test-unit', '~> 2.4')
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {spec,tests}/*`.split("\n")
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lineman
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Todd Willey (xtoddx)
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: test-unit
|
16
|
+
requirement: &70263140719340 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.4'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70263140719340
|
25
|
+
description: Lineman provides clean output for your tests, giving you one line for
|
26
|
+
each test that shows the name along with some statistics.
|
27
|
+
email: xtoddx@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README.md
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/test/unit/runner/statistics.rb
|
35
|
+
- lib/test/unit/ui/statistics.rb
|
36
|
+
- lib/test/unit/ui/statistics/base_runner.rb
|
37
|
+
- lib/test/unit/ui/statistics/runners/lineman.rb
|
38
|
+
- lineman.gemspec
|
39
|
+
homepage: http://github.com/xtoddx/lineman
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --charset=UTF-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.17
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: A test runner (UI) for test-unit
|
64
|
+
test_files: []
|
65
|
+
has_rdoc:
|