tunit 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +113 -0
- data/Rakefile +9 -0
- data/example_spec.rb +35 -0
- data/example_test.rb +23 -0
- data/lib/tunit.rb +83 -0
- data/lib/tunit/assertion_errors.rb +43 -0
- data/lib/tunit/assertions.rb +68 -0
- data/lib/tunit/autorun.rb +8 -0
- data/lib/tunit/compound_reporter.rb +33 -0
- data/lib/tunit/hooks.rb +11 -0
- data/lib/tunit/progress_reporter.rb +11 -0
- data/lib/tunit/reporter.rb +39 -0
- data/lib/tunit/runnable.rb +51 -0
- data/lib/tunit/spec.rb +77 -0
- data/lib/tunit/summary_reporter.rb +50 -0
- data/lib/tunit/test.rb +104 -0
- data/lib/tunit/version.rb +3 -0
- data/test/rtest_test.rb +19 -0
- data/test/tunit/assertion_errors_test.rb +95 -0
- data/test/tunit/assertions_test.rb +68 -0
- data/test/tunit/progress_reporter_test.rb +45 -0
- data/test/tunit/reporter_test.rb +66 -0
- data/test/tunit/runnable_test.rb +69 -0
- data/test/tunit/spec_test.rb +57 -0
- data/test/tunit/summary_reporter_test.rb +116 -0
- data/test/tunit/test_case.rb +72 -0
- data/test/tunit/test_test.rb +181 -0
- data/tunit.gemspec +24 -0
- metadata +127 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'tunit/reporter'
|
2
|
+
|
3
|
+
module Tunit
|
4
|
+
class CompoundReporter < Reporter
|
5
|
+
def initialize *reporters
|
6
|
+
super()
|
7
|
+
self.reporters = reporters
|
8
|
+
end
|
9
|
+
attr_accessor :reporters
|
10
|
+
|
11
|
+
def << reporter
|
12
|
+
self.reporters << reporter
|
13
|
+
end
|
14
|
+
|
15
|
+
def passed?
|
16
|
+
self.reporters.all?(&:passed?)
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
self.reporters.each(&:start)
|
21
|
+
end
|
22
|
+
|
23
|
+
def record result
|
24
|
+
self.reporters.each do |reporter|
|
25
|
+
reporter.record result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def report
|
30
|
+
self.reporters.each(&:report)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/tunit/hooks.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'tunit/reporter'
|
2
|
+
|
3
|
+
module Tunit
|
4
|
+
class ProgressReporter < Reporter
|
5
|
+
def record result
|
6
|
+
io.print result.code
|
7
|
+
io.print " = %s#%s (%.2f s)" % [result.class, result.name, result.time] if options[:verbose]
|
8
|
+
io.puts if options[:verbose]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Tunit
|
2
|
+
class Reporter
|
3
|
+
def initialize io = $stdout, options = {}
|
4
|
+
self.io = io
|
5
|
+
self.options = options
|
6
|
+
self.assertions = 0
|
7
|
+
self.count = 0
|
8
|
+
self.results = []
|
9
|
+
end
|
10
|
+
attr_accessor :io, :options, :assertions, :count, :results
|
11
|
+
attr_accessor :start_time, :total_time
|
12
|
+
attr_accessor :failures, :skips, :errors
|
13
|
+
|
14
|
+
def start
|
15
|
+
self.start_time = Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def record result
|
19
|
+
self.count += 1
|
20
|
+
self.assertions += result.assertions
|
21
|
+
|
22
|
+
self.results << result if !result.passed? || result.skipped?
|
23
|
+
end
|
24
|
+
|
25
|
+
def passed?
|
26
|
+
results.all?(&:skipped?)
|
27
|
+
end
|
28
|
+
|
29
|
+
def report
|
30
|
+
total = results.group_by {|r| r.failure.class }
|
31
|
+
total.default = []
|
32
|
+
|
33
|
+
self.total_time = Time.now - start_time
|
34
|
+
self.failures = total[Assertion].size
|
35
|
+
self.failures += total[Empty].size
|
36
|
+
self.skips = total[Skip].size
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Tunit
|
2
|
+
class Runnable
|
3
|
+
def self.runnable_methods
|
4
|
+
raise NotImplementedError, "subclass responsibility"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.runnables
|
8
|
+
@@runnables ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.runnables= runnable
|
12
|
+
@@runnables = [runnable].flatten
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.inherited klass
|
16
|
+
self.runnables << klass
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.run reporter, options = {}
|
21
|
+
filter = options.fetch(:filter) { '/./' }
|
22
|
+
filter = Regexp.new $1 if filter =~ /\/(.*)\//
|
23
|
+
|
24
|
+
filtered_methods = self.runnable_methods.select { |m|
|
25
|
+
filter === m || filter === "#{self}##{m}"
|
26
|
+
}
|
27
|
+
|
28
|
+
filtered_methods.each { |test|
|
29
|
+
reporter.record self.new(test).run
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize name
|
34
|
+
self.name = name
|
35
|
+
self.assertions = 0
|
36
|
+
self.failures = []
|
37
|
+
end
|
38
|
+
attr_accessor :name, :assertions, :failures, :time
|
39
|
+
|
40
|
+
|
41
|
+
def run
|
42
|
+
fail NotImplementedError, "subclass responsibility"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def self.methods_matching re
|
48
|
+
public_instance_methods(true).grep(re).map(&:to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/tunit/spec.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'tunit/test'
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
# Override describe to avoid warnings and collisions with minitest/spec
|
5
|
+
alias_method :_old_describe, :describe if defined? Minitest
|
6
|
+
def describe desc, &blk
|
7
|
+
_old_describe desc, &blk if defined? Minitest
|
8
|
+
|
9
|
+
super_klass = if Class === self && is_a?(Tunit::Spec::DSL)
|
10
|
+
self
|
11
|
+
else
|
12
|
+
Tunit::Spec
|
13
|
+
end
|
14
|
+
|
15
|
+
klass = super_klass.create desc
|
16
|
+
klass.class_eval(&blk)
|
17
|
+
klass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Tunit
|
22
|
+
class Spec < Test
|
23
|
+
module DSL
|
24
|
+
attr_reader :name
|
25
|
+
|
26
|
+
def before &block
|
27
|
+
define_method :setup do
|
28
|
+
self.instance_eval(&block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def after &block
|
33
|
+
define_method :teardown do
|
34
|
+
self.instance_eval(&block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def it desc, &block
|
39
|
+
block ||= -> { skip "(no tests defined)" }
|
40
|
+
|
41
|
+
@specs ||= 0
|
42
|
+
@specs += 1
|
43
|
+
|
44
|
+
test_name = desc.gsub " ", "_"
|
45
|
+
name = "test_%04d_%s" % [ @specs, test_name ]
|
46
|
+
|
47
|
+
define_method name, &block
|
48
|
+
|
49
|
+
name
|
50
|
+
end
|
51
|
+
|
52
|
+
def children
|
53
|
+
@children ||= []
|
54
|
+
end
|
55
|
+
|
56
|
+
def create name
|
57
|
+
klass = Class.new(self) {
|
58
|
+
@name = name
|
59
|
+
|
60
|
+
remove_test_methods!
|
61
|
+
}
|
62
|
+
|
63
|
+
children << klass
|
64
|
+
|
65
|
+
klass
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_test_methods!
|
69
|
+
self.runnable_methods.map { |test|
|
70
|
+
self.send :undef_method, test
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
extend DSL
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'tunit/reporter'
|
2
|
+
|
3
|
+
module Tunit
|
4
|
+
class SummaryReporter < Reporter
|
5
|
+
SKIP_MSG = "\n\nYou have skipped tests. Run with --verbose for details."
|
6
|
+
|
7
|
+
def start
|
8
|
+
super
|
9
|
+
|
10
|
+
io.puts "Run options: #{options.inspect}"
|
11
|
+
io.puts
|
12
|
+
io.puts "# Running:"
|
13
|
+
io.puts
|
14
|
+
end
|
15
|
+
|
16
|
+
def report
|
17
|
+
super
|
18
|
+
|
19
|
+
io.puts unless options[:verbose]
|
20
|
+
io.puts
|
21
|
+
io.puts statistics
|
22
|
+
io.puts aggregated_results
|
23
|
+
io.puts summary
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def statistics
|
29
|
+
"Finished in %.6fs, %.4f runs/s, %.4f assertions/s." %
|
30
|
+
[total_time, count / total_time, assertions / total_time]
|
31
|
+
end
|
32
|
+
|
33
|
+
def aggregated_results
|
34
|
+
filtered_results = results.dup
|
35
|
+
filtered_results.reject!(&:skipped?) unless options[:verbose]
|
36
|
+
|
37
|
+
filtered_results.each_with_index.map do |result, index|
|
38
|
+
"\n%3d) %s" % [index + 1, result]
|
39
|
+
end.join + "\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
def summary
|
43
|
+
extra = ""
|
44
|
+
extra << SKIP_MSG if results.any?(&:skipped?) && !options[:verbose]
|
45
|
+
|
46
|
+
"%d runs, %d assertions, %d failures, %d skips%s" %
|
47
|
+
[count, assertions, failures, skips, extra]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/tunit/test.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'tunit/runnable'
|
2
|
+
require 'tunit/assertions'
|
3
|
+
require 'tunit/hooks'
|
4
|
+
|
5
|
+
module Tunit
|
6
|
+
class Test < Runnable
|
7
|
+
include Assertions
|
8
|
+
include Hooks
|
9
|
+
PREFIX = /^test_/
|
10
|
+
|
11
|
+
def self.runnable_methods
|
12
|
+
methods = methods_matching PREFIX
|
13
|
+
case self.test_order
|
14
|
+
when :random
|
15
|
+
max = methods.size
|
16
|
+
methods.sort.sort_by { rand max }
|
17
|
+
when :alpha
|
18
|
+
methods.sort
|
19
|
+
else
|
20
|
+
raise "Unknown test_order: #{self.test_order.inspect}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Randomize tests by default
|
25
|
+
def self.test_order
|
26
|
+
:random
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.order!
|
30
|
+
class << self
|
31
|
+
undef_method :test_order if method_defined?(:test_order)
|
32
|
+
define_method(:test_order) { :alpha }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def run
|
37
|
+
capture_exceptions do
|
38
|
+
time_it do
|
39
|
+
setup
|
40
|
+
send name
|
41
|
+
end
|
42
|
+
|
43
|
+
if self.assertions.zero?
|
44
|
+
begin
|
45
|
+
fail ::Tunit::Empty, "Empty test, '#{self.to_s}'"
|
46
|
+
rescue ::Tunit::Empty => e
|
47
|
+
method_obj = self.method(name)
|
48
|
+
e.location = method_obj.source_location.join(":")
|
49
|
+
raise e
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
capture_exceptions do
|
55
|
+
teardown
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def passed?
|
61
|
+
!failure
|
62
|
+
end
|
63
|
+
|
64
|
+
def skipped?
|
65
|
+
failure && Skip === failure
|
66
|
+
end
|
67
|
+
|
68
|
+
def failure
|
69
|
+
self.failures.first
|
70
|
+
end
|
71
|
+
|
72
|
+
def code
|
73
|
+
failure && failure.result_code || "."
|
74
|
+
end
|
75
|
+
|
76
|
+
def location
|
77
|
+
loc = " [#{self.failure.location}]" unless passed?
|
78
|
+
"#{self.class}##{self.name}#{loc}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
return location if passed? && !skipped?
|
83
|
+
|
84
|
+
failures.map { |failure|
|
85
|
+
"#{failure.result_label}:\n#{self.location}:\n#{failure.message}\n"
|
86
|
+
}.join "\n"
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def time_it
|
92
|
+
t0 = Time.now
|
93
|
+
yield
|
94
|
+
ensure
|
95
|
+
self.time = Time.now - t0
|
96
|
+
end
|
97
|
+
|
98
|
+
def capture_exceptions
|
99
|
+
yield
|
100
|
+
rescue Assertion => e
|
101
|
+
self.failures << e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/test/rtest_test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative 'tunit/test_case'
|
2
|
+
require 'tunit'
|
3
|
+
|
4
|
+
class TunitTest < Tunit::TestCase
|
5
|
+
def setup
|
6
|
+
Tunit.io = io
|
7
|
+
Tunit::Runnable.runnables = [PassingTest]
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_run_gathers_reporters_under_compound_reporter
|
11
|
+
Tunit.run
|
12
|
+
assert_instance_of Tunit::CompoundReporter, Tunit.reporter
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_run_dispatches_the_reporters_to_run
|
16
|
+
Tunit.run
|
17
|
+
assert Tunit.reporter.passed?
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_relative 'test_case'
|
2
|
+
require 'tunit/assertion_errors'
|
3
|
+
require 'tunit/test'
|
4
|
+
|
5
|
+
module Tunit
|
6
|
+
class AssertionErrotunit < TestCase
|
7
|
+
def setup
|
8
|
+
self.assertion = Assertion.new
|
9
|
+
end
|
10
|
+
attr_accessor :assertion
|
11
|
+
|
12
|
+
def test_error
|
13
|
+
assert_instance_of Assertion, assertion.error
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_location
|
17
|
+
result = FailingTest.new.run
|
18
|
+
assertion = result.failure
|
19
|
+
exp_location = %r(.*/tunit/test/tunit/test_case.rb:\d{1,})
|
20
|
+
|
21
|
+
assert_match exp_location, assertion.location
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_result_code
|
25
|
+
assert_equal "F", assertion.result_code
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_result_label
|
29
|
+
assert_equal "Failure", assertion.result_label
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class EmptyTest < TestCase
|
34
|
+
def setup
|
35
|
+
self.assertion = Empty.new
|
36
|
+
end
|
37
|
+
attr_accessor :assertion
|
38
|
+
|
39
|
+
def test_error
|
40
|
+
assert_instance_of Empty, assertion.error
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_location
|
44
|
+
result = FailingTest.new(:test_empty).run
|
45
|
+
assertion = result.failure
|
46
|
+
exp_location = %r(.*/tunit/test/tunit/test_case.rb:\d{1,})
|
47
|
+
|
48
|
+
assert_match exp_location, assertion.location
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_location_handles_unnamed_classes
|
52
|
+
result = Class.new(Test) {
|
53
|
+
def test_empty
|
54
|
+
end
|
55
|
+
}.new(:test_empty).run
|
56
|
+
|
57
|
+
refute_equal :not_set, result.failure.location
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_result_code
|
61
|
+
assert_equal "_", assertion.result_code
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_result_label
|
65
|
+
assert_equal "Empty", assertion.result_label
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class SkipTest < TestCase
|
70
|
+
def setup
|
71
|
+
self.assertion = Skip.new
|
72
|
+
end
|
73
|
+
attr_accessor :assertion
|
74
|
+
|
75
|
+
def test_error
|
76
|
+
assert_instance_of Skip, assertion.error
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_location
|
80
|
+
result = SkippedTest.new.run
|
81
|
+
assertion = result.failure
|
82
|
+
exp_location = %r(.*/tunit/test/tunit/test_case.rb:\d{1,})
|
83
|
+
|
84
|
+
assert_match exp_location, assertion.location
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_result_code
|
88
|
+
assert_equal "S", assertion.result_code
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_result_label
|
92
|
+
assert_equal "Skipped", assertion.result_label
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|