baretest 0.1.0

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,31 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+ class Run
11
+ module Minimal
12
+ def run_all(*args)
13
+ start = Time.now
14
+ super # run all suites
15
+ stop = Time.now
16
+ values = @count.values_at(:test, :success, :pending, :failure, :error)
17
+ values.push(stop-start, global_status)
18
+ printf "Tests: %d\n" \
19
+ "Success: %d\n" \
20
+ "Pending: %d\n" \
21
+ "Failures: %d\n" \
22
+ "Errors: %d\n" \
23
+ "Time: %f\n" \
24
+ "Status: %s\n",
25
+ *values
26
+ end
27
+ end
28
+ end
29
+
30
+ @format["test/run/minimal"] = Run::Minimal # register the extender
31
+ end
@@ -0,0 +1,32 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+ class Run
11
+ module Spec
12
+ def run_all
13
+ @depth = 0
14
+ super
15
+ end
16
+
17
+ def run_suite(suite)
18
+ return super unless suite.description
19
+ puts("\n"+' '*@depth+suite.description)
20
+ @depth += 1
21
+ super
22
+ @depth -= 1
23
+ end
24
+
25
+ def run_test(assertion)
26
+ puts(' '*@depth+assertion.description)
27
+ end
28
+ end
29
+ end
30
+
31
+ @format["test/run/spec"] = Run::Spec
32
+ end
@@ -0,0 +1,32 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+ class Run
11
+ module TAP
12
+ def run_all
13
+ puts "TAP version 13"
14
+ count = proc { |acc,csuite| acc+csuite.tests.size+csuite.suites.inject(0, &count) }
15
+ puts "1..#{count[0, suite]}"
16
+ @current = 0
17
+ super
18
+ end
19
+
20
+ def run_test(assertion)
21
+ rv = super
22
+ printf "%sok %d - %s%s\n",
23
+ rv.status == :success ? '' : 'not ',
24
+ @current+=1,
25
+ rv.description,
26
+ rv.status == :success ? '' : " # #{rv.status}"
27
+ end
28
+ end
29
+ end
30
+
31
+ @format["test/run/tap"] = Run::TAP
32
+ end
@@ -0,0 +1,56 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+ class Run
11
+ module XML
12
+ def run_all
13
+ @depth = 1
14
+
15
+ puts '<?xml version="1.0" encoding="utf-8"?>'
16
+ puts '<tests>'
17
+ start = Time.now
18
+ super
19
+ stop = Time.now
20
+ status = case
21
+ when @count[:error] > 0 then 'error'
22
+ when @count[:failure] > 0 then 'failure'
23
+ when @count[:pending] > 0 then 'incomplete'
24
+ when @count[:skipped] > 0 then 'incomplete'
25
+ else 'success'
26
+ end
27
+ puts %{</tests>}
28
+ puts %{<report>}
29
+ puts %{\t<duration>#{stop-start}</duration>}
30
+ @count.each { |key, value|
31
+ puts %{\t<count type="#{key}">#{value}</count>}
32
+ }
33
+ puts %{</report>}
34
+ puts %{<status>#{status}</status>}
35
+ end
36
+
37
+ def run_suite(suite)
38
+ puts %{#{"\t"*@depth}<suite description="#{suite.description}">}
39
+ @depth += 1
40
+ super
41
+ @depth -= 1
42
+ puts %{#{"\t"*@depth}</suite>}
43
+ end
44
+
45
+ def run_test(assertion)
46
+ rv = super
47
+ puts %{#{"\t"*@depth}<test>}
48
+ puts %{#{"\t"*@depth}\t<status>#{rv.status}</status>}
49
+ puts %{#{"\t"*@depth}\t<description>#{rv.description}</description>}
50
+ puts %{#{"\t"*@depth}</test>}
51
+ end
52
+ end
53
+ end
54
+
55
+ @format["test/run/xml"] = Run::XML
56
+ end
data/lib/test/run.rb ADDED
@@ -0,0 +1,137 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+
11
+ # Run is the envorionment in which the suites and asserts are executed.
12
+ # Prior to the execution, the Run instance extends itself with the
13
+ # formatter given.
14
+ # Your formatter can override:
15
+ # * run_all
16
+ # * run_suite
17
+ # * run_test
18
+ class Run
19
+ # The toplevel suite.
20
+ attr_reader :suite
21
+
22
+ # The initialisation blocks of extenders
23
+ attr_reader :inits
24
+
25
+ # Some statistics, standard count keys are:
26
+ # * :test - the number of tests executed until now
27
+ # * :suite - the number of suites executed until now
28
+ # * :success - the number of tests with status :success
29
+ # * :failure - the number of tests with status :failure
30
+ # * :pending - the number of tests with status :pending
31
+ # * :skipped - the number of tests with status :skipped
32
+ # * :error - the number of tests with status :error
33
+ attr_reader :count
34
+
35
+ # Run the passed suite.
36
+ # Calls run_all with the toplevel suite as argument and a block that
37
+ # calls run_suite with the yielded argument (which should be the toplevel
38
+ # suite).
39
+ # Options accepted:
40
+ # * :extenders: An Array of Modules, will be used as argument to self.extend, useful e.g. for
41
+ # mock integration
42
+ # * :format: A string with the basename (without suffix) of the formatter to use - or a
43
+ # Module
44
+ # * :interactive: true/false, will switch this Test::Run instance into IRB mode, where an error
45
+ # will cause an irb session to be started in the context of a clean copy of
46
+ # the assertion with all setup callbacks invoked
47
+ #
48
+ # The order of extensions is:
49
+ # * :extender
50
+ # * :format (extends with the formatter module)
51
+ # * :interactive (extends with IRBMode)
52
+ def initialize(suite, opts=nil)
53
+ @suite = suite
54
+ @inits = []
55
+ @options = opts || {}
56
+ @count = @options[:count] || Hash.new(0)
57
+
58
+ (Test.extender+Array(@options[:extender])).each do |extender|
59
+ extend(extender)
60
+ end
61
+
62
+ # Extend with the output formatter
63
+ if format = @options[:format] then
64
+ require "test/run/#{format}" if String === format
65
+ extend(String === format ? Test.format["test/run/#{format}"] : format)
66
+ end
67
+
68
+ # Extend with irb dropout code
69
+ extend(Test::IRBMode) if @options[:interactive]
70
+
71
+ # Initialize extenders
72
+ @inits.each { |init| instance_eval(&init) }
73
+ end
74
+
75
+ # Hook initializers for extenders.
76
+ # Blocks passed to init will be instance_eval'd at the end of initialize.
77
+ # Example usage:
78
+ # module ExtenderForRun
79
+ # def self.extended(run_obj)
80
+ # run_obj.init do
81
+ # # do some initialization stuff for this module
82
+ # end
83
+ # end
84
+ # end
85
+ def init(&block)
86
+ @inits << block
87
+ end
88
+
89
+ # Formatter callback.
90
+ # Invoked once at the beginning.
91
+ # Gets the toplevel suite as single argument.
92
+ def run_all
93
+ run_suite(@suite)
94
+ end
95
+
96
+ # Formatter callback.
97
+ # Invoked once for every suite.
98
+ # Gets the suite to run as single argument.
99
+ # Runs all assertions and nested suites.
100
+ def run_suite(suite)
101
+ suite.tests.each do |test|
102
+ run_test(test)
103
+ end
104
+ suite.suites.each do |suite|
105
+ run_suite(suite)
106
+ end
107
+ @count[:suite] += 1
108
+ end
109
+
110
+ # Formatter callback.
111
+ # Invoked once for every assertion.
112
+ # Gets the assertion to run as single argument.
113
+ def run_test(assertion)
114
+ rv = assertion.execute
115
+ @count[:test] += 1
116
+ @count[assertion.status] += 1
117
+ rv
118
+ end
119
+
120
+ # Status over all tests ran up to now
121
+ # Can be :error, :failure, :incomplete or :success
122
+ # The algorithm is a simple fall through:
123
+ # if any test errored, then global_status is :error,
124
+ # if not, then if any test failed, global_status is :failure,
125
+ # if not, then if any test was pending or skipped, global_status is :incomplete,
126
+ # if not, then global_status is success
127
+ def global_status
128
+ case
129
+ when @count[:error] > 0 then :error
130
+ when @count[:failure] > 0 then :failure
131
+ when @count[:pending] > 0 then :incomplete
132
+ when @count[:skipped] > 0 then :incomplete
133
+ else :success
134
+ end
135
+ end
136
+ end
137
+ end
data/lib/test/suite.rb ADDED
@@ -0,0 +1,95 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+
11
+ # A Suite is a container for multiple assertions.
12
+ # You can give a suite a description, also a suite can contain
13
+ # setup and teardown blocks that are executed before (setup) and after
14
+ # (teardown) every assertion.
15
+ # Suites can also be nested. Nested suites will inherit setup and teardown.
16
+ class Suite
17
+
18
+ # Nested suites
19
+ attr_reader :suites
20
+
21
+ # All assertions in this suite
22
+ attr_reader :tests
23
+
24
+ # This suites description. Toplevel suites usually don't have a description.
25
+ attr_reader :description
26
+
27
+ # This suites direct parent. Nil if toplevel suite.
28
+ attr_reader :parent
29
+
30
+ # An Array containing the suite itself (first element), then its direct
31
+ # parent suite, then that suite's parent and so on
32
+ attr_reader :ancestors
33
+
34
+ def self.create(description=nil, parent=nil, opts={}, &block)
35
+ Array(opts[:requires]).each { |file| require file } if opts[:requires]
36
+ rescue LoadError
37
+ # A suite is skipped if requirements are not met
38
+ Skipped::Suite.new(description, parent, &block)
39
+ else
40
+ # All suites within Skipped::Suite are Skipped::Suite
41
+ (block ? self : Skipped::Suite).new(description, parent, &block)
42
+ end
43
+
44
+ def initialize(description=nil, parent=nil, &block)
45
+ @description = description
46
+ @parent = parent
47
+ @suites = []
48
+ @tests = []
49
+ @setup = []
50
+ @teardown = []
51
+ @ancestors = [self] + (@parent ? @parent.ancestors : [])
52
+ instance_eval(&block) if block
53
+ end
54
+
55
+ # Define a nested suite.
56
+ # Nested suites inherit setup & teardown methods.
57
+ # Also if an outer suite is skipped, all inner suites are skipped too.
58
+ # Valid values for opts:
59
+ # requires
60
+ # : A list of files to require, if one of the requires fails, the suite
61
+ # will be skipped. Accepts a String or an Array
62
+ def suite(description=nil, opts={}, &block)
63
+ @suites << self.class.create(description, self, opts, &block)
64
+ end
65
+
66
+ # All setups in the order of their nesting (outermost first, innermost last)
67
+ def ancestry_setup
68
+ ancestors.map { |suite| suite.setup }.flatten.reverse
69
+ end
70
+
71
+ # All teardowns in the order of their nesting (innermost first, outermost last)
72
+ def ancestry_teardown
73
+ ancestors.map { |suite| suite.teardown }.flatten
74
+ end
75
+
76
+ # Define a setup block for this suite. The block will be ran before every
77
+ # assertion once, even for nested suites.
78
+ def setup(&block)
79
+ block ? @setup << block : @setup
80
+ end
81
+
82
+ # Define a teardown block for this suite. The block will be ran after every
83
+ # assertion once, even for nested suites.
84
+ def teardown(&block)
85
+ block ? @teardown << block : @teardown
86
+ end
87
+
88
+ # Define an assertion. The block is supposed to return a trueish value
89
+ # (anything but nil or false).
90
+ # See Assertion for more info.
91
+ def assert(description=nil, &block)
92
+ @tests << Assertion.new(self, description, &block)
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,19 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ module Test
10
+ module VERSION
11
+ MAJOR = 0
12
+ MINOR = 1
13
+ TINY = 0
14
+
15
+ def self.to_s
16
+ "#{MAJOR}.#{MINOR||0}.#{TINY||0}"
17
+ end
18
+ end
19
+ end
data/lib/test.rb ADDED
@@ -0,0 +1,118 @@
1
+ #--
2
+ # Copyright 2009 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
9
+ require 'test/irb_mode'
10
+ require 'test/run'
11
+ require 'test/suite'
12
+ require 'test/assertion'
13
+ # See bottom for more requires
14
+
15
+
16
+
17
+ module Test
18
+ class <<self
19
+ # A hash of formatters (require-string => module) to be used with Test::Run.
20
+ attr_reader :format
21
+
22
+ # For mock integration and others, append modules that should extend the Test::Run instance.
23
+ attr_reader :extender
24
+
25
+ # The toplevel suite. That's the one run_if_mainfile and define add suites
26
+ # and assertions to.
27
+ attr_reader :toplevel_suite
28
+
29
+ # The full path to this file
30
+ attr_reader :required_file
31
+ end
32
+
33
+ # For bootstrapped selftest
34
+ def self.init
35
+ @format = {}
36
+ @extender = []
37
+ @toplevel_suite = Suite.new
38
+ @required_file = ["", *$LOAD_PATH].map { |path|
39
+ File.expand_path(File.join(path, __FILE__))
40
+ }.find { |full| File.exist?(full) }
41
+ end
42
+ init
43
+
44
+ # Adds the contained assertions and suites to the toplevel suite
45
+ def self.define(name=nil, opts={}, &block)
46
+ if name then
47
+ @toplevel_suite.suite(name, opts, &block)
48
+ elsif opts && !opts.empty?
49
+ raise ArgumentError, "Suites with options must have names"
50
+ else
51
+ @toplevel_suite.instance_eval(&block)
52
+ end
53
+ end
54
+
55
+ # Creates a Test::Run instance, adds the assertions and suites defined in its
56
+ # own block to that Test::Run instance's toplevel suite and if $PROGRAM_NAME
57
+ # (aka $0) is equal to __FILE__ (means the current file is the file directly
58
+ # executed by ruby, and not just required/loaded/evaled by another file),
59
+ # subsequently also runs that suite.
60
+ def self.run_if_mainfile(name=nil, opts={}, &block)
61
+ define(name, opts, &block)
62
+ if caller.first[/^[^:]*/] == $0 then # if is mainfile
63
+ run(:format => ENV['FORMAT'], :interactive => ENV['INTERACTIVE'])
64
+ end
65
+ end
66
+
67
+ def self.run(opts=nil)
68
+ Run.new(@toplevel_suite, opts).run_all
69
+ end
70
+
71
+ # Skipped contains variants of Suite and Assertion.
72
+ # See Skipped::Suite and Skipped::Assertion
73
+ module Skipped
74
+ # Like Test::Suite, but all Assertions are defined as Skipped::Assertion
75
+ class Suite < ::Test::Suite
76
+ # :nodoc:
77
+ # All Assertions use Skipped::Assertion instead of Test::Assertion.
78
+ def assert(description=nil, &block)
79
+ @tests << Skipped::Assertion.new(self, description, &block)
80
+ end
81
+
82
+ # :nodoc:
83
+ # All setup blocks are disabled
84
+ def ancestry_setup
85
+ []
86
+ end
87
+
88
+ # :nodoc:
89
+ # All teardown blocks are disabled
90
+ def ancestry_teardown
91
+ []
92
+ end
93
+
94
+ # :nodoc:
95
+ # All setup blocks are disabled
96
+ def setup(&block)
97
+ []
98
+ end
99
+
100
+ # :nodoc:
101
+ # All teardown blocks are disabled
102
+ def teardown(&block)
103
+ []
104
+ end
105
+ end
106
+
107
+ # Like Test::Assertion, but fakes execution and sets status always to
108
+ # skipped.
109
+ class Assertion < ::Test::Assertion
110
+ def execute() @status = :skipped and self end
111
+ end
112
+ end
113
+ end
114
+
115
+
116
+
117
+ # At bottom due to dependencies
118
+ require 'test/assertion/support' # Needs Test.extender to be defined
@@ -0,0 +1,5 @@
1
+ Test.run_if_mainfile do
2
+ assert "Sample assert" do
3
+ true
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require File.expand_path("#{__FILE__}/../bootstraptest.rb")
2
+ puts "Done"
File without changes