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.
@@ -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
@@ -0,0 +1,11 @@
1
+ module Tunit
2
+ module Hooks
3
+ # Runs before each test
4
+ def setup
5
+ end
6
+
7
+ # Runs after each test
8
+ def teardown
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Tunit
2
+ VERSION = "0.0.1"
3
+ end
@@ -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