tunit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|