betatest 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,283 @@
1
+ require "betatest" unless defined? Betatest::Runnable
2
+
3
+ module Betatest
4
+ ##
5
+ # Subclass Test to create your own tests. Typically you'll want a
6
+ # Test subclass per implementation class.
7
+ #
8
+ # See Betatest::Assertions
9
+
10
+ class Test < Runnable
11
+ require "betatest/assertions"
12
+ include Betatest::Assertions
13
+
14
+ PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, # :nodoc:
15
+ Interrupt, SystemExit]
16
+
17
+ class << self; attr_accessor :io_lock; end
18
+ self.io_lock = Mutex.new
19
+
20
+ ##
21
+ # Call this at the top of your tests when you absolutely
22
+ # positively need to have ordered tests. In doing so, you're
23
+ # admitting that you suck and your tests are weak.
24
+
25
+ def self.i_suck_and_my_tests_are_order_dependent!
26
+ class << self
27
+ undef_method :test_order if method_defined? :test_order
28
+ define_method :test_order do :alpha end
29
+ end
30
+ end
31
+
32
+ ##
33
+ # Make diffs for this Test use #pretty_inspect so that diff
34
+ # in assert_equal can have more details. NOTE: this is much slower
35
+ # than the regular inspect but much more usable for complex
36
+ # objects.
37
+
38
+ def self.make_my_diffs_pretty!
39
+ require "pp"
40
+
41
+ define_method :mu_pp do |o|
42
+ o.pretty_inspect
43
+ end
44
+ end
45
+
46
+ ##
47
+ # Call this at the top of your tests when you want to run your
48
+ # tests in parallel. In doing so, you're admitting that you rule
49
+ # and your tests are awesome.
50
+
51
+ def self.parallelize_me!
52
+ include Betatest::Parallel::Test
53
+ extend Betatest::Parallel::Test::ClassMethods
54
+ end
55
+
56
+ ##
57
+ # Returns all instance methods starting with "test_". Based on
58
+ # #test_order, the methods are either sorted, randomized
59
+ # (default), or run in parallel.
60
+
61
+ def self.runnable_methods
62
+ methods = methods_matching(/^test_/)
63
+
64
+ case self.test_order
65
+ when :random, :parallel then
66
+ max = methods.size
67
+ methods.sort.sort_by { rand max }
68
+ when :alpha, :sorted then
69
+ methods.sort
70
+ else
71
+ raise "Unknown test_order: #{self.test_order.inspect}"
72
+ end
73
+ end
74
+
75
+ ##
76
+ # Defines the order to run tests (:random by default). Override
77
+ # this or use a convenience method to change it for your tests.
78
+
79
+ def self.test_order
80
+ :random
81
+ end
82
+
83
+ ##
84
+ # The time it took to run this test.
85
+
86
+ attr_accessor :time
87
+
88
+ def marshal_dump # :nodoc:
89
+ super << self.time
90
+ end
91
+
92
+ def marshal_load ary # :nodoc:
93
+ self.time = ary.pop
94
+ super
95
+ end
96
+
97
+ ##
98
+ # Runs a single test with setup/teardown hooks.
99
+
100
+ def run
101
+ with_info_handler do
102
+ time_it do
103
+ capture_exceptions do
104
+ before_setup; setup; after_setup
105
+
106
+ self.send self.name
107
+ end
108
+
109
+ %w{ before_teardown teardown after_teardown }.each do |hook|
110
+ capture_exceptions do
111
+ self.send hook
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ self # per contract
118
+ end
119
+
120
+ ##
121
+ # Provides before/after hooks for setup and teardown. These are
122
+ # meant for library writers, NOT for regular test authors. See
123
+ # #before_setup for an example.
124
+
125
+ module LifecycleHooks
126
+
127
+ ##
128
+ # Runs before every test, before setup. This hook is meant for
129
+ # libraries to extend betatest. It is not meant to be used by
130
+ # test developers.
131
+ #
132
+ # As a simplistic example:
133
+ #
134
+ # module MyBetatestPlugin
135
+ # def before_setup
136
+ # super
137
+ # # ... stuff to do before setup is run
138
+ # end
139
+ #
140
+ # def after_setup
141
+ # # ... stuff to do after setup is run
142
+ # super
143
+ # end
144
+ #
145
+ # def before_teardown
146
+ # super
147
+ # # ... stuff to do before teardown is run
148
+ # end
149
+ #
150
+ # def after_teardown
151
+ # # ... stuff to do after teardown is run
152
+ # super
153
+ # end
154
+ # end
155
+ #
156
+ # class MiniTest::Test
157
+ # include MyBetatestPlugin
158
+ # end
159
+
160
+ def before_setup; end
161
+
162
+ ##
163
+ # Runs before every test. Use this to set up before each test
164
+ # run.
165
+
166
+ def setup; end
167
+
168
+ ##
169
+ # Runs before every test, after setup. This hook is meant for
170
+ # libraries to extend betatest. It is not meant to be used by
171
+ # test developers.
172
+ #
173
+ # See #before_setup for an example.
174
+
175
+ def after_setup; end
176
+
177
+ ##
178
+ # Runs after every test, before teardown. This hook is meant for
179
+ # libraries to extend betatest. It is not meant to be used by
180
+ # test developers.
181
+ #
182
+ # See #before_setup for an example.
183
+
184
+ def before_teardown; end
185
+
186
+ ##
187
+ # Runs after every test. Use this to clean up after each test
188
+ # run.
189
+
190
+ def teardown; end
191
+
192
+ ##
193
+ # Runs after every test, after teardown. This hook is meant for
194
+ # libraries to extend betatest. It is not meant to be used by
195
+ # test developers.
196
+ #
197
+ # See #before_setup for an example.
198
+
199
+ def after_teardown; end
200
+ end # LifecycleHooks
201
+
202
+ def capture_exceptions # :nodoc:
203
+ begin
204
+ yield
205
+ rescue *PASSTHROUGH_EXCEPTIONS
206
+ raise
207
+ rescue Assertion => e
208
+ self.failures << e
209
+ rescue Exception => e
210
+ self.failures << UnexpectedError.new(e)
211
+ end
212
+ end
213
+
214
+ ##
215
+ # Did this run error?
216
+
217
+ def error?
218
+ self.failures.any? { |f| UnexpectedError === f }
219
+ end
220
+
221
+ ##
222
+ # The location identifier of this test.
223
+
224
+ def location
225
+ loc = " [#{self.failure.location}]" unless passed? or error?
226
+ "#{self.class}##{self.name}#{loc}"
227
+ end
228
+
229
+ ##
230
+ # Did this run pass?
231
+ #
232
+ # Note: skipped runs are not considered passing, but they don't
233
+ # cause the process to exit non-zero.
234
+
235
+ def passed?
236
+ not self.failure
237
+ end
238
+
239
+ ##
240
+ # Returns ".", "F", or "E" based on the result of the run.
241
+
242
+ def result_code
243
+ self.failure and self.failure.result_code or "."
244
+ end
245
+
246
+ ##
247
+ # Was this run skipped?
248
+
249
+ def skipped?
250
+ self.failure and Skip === self.failure
251
+ end
252
+
253
+ def time_it # :nodoc:
254
+ t0 = Time.now
255
+
256
+ yield
257
+ ensure
258
+ self.time = Time.now - t0
259
+ end
260
+
261
+ def to_s # :nodoc:
262
+ return location if passed? and not skipped?
263
+
264
+ failures.map { |failure|
265
+ "#{failure.result_label}:\n#{self.location}:\n#{failure.message}\n"
266
+ }.join "\n"
267
+ end
268
+
269
+ def with_info_handler &block # :nodoc:
270
+ t0 = Time.now
271
+
272
+ handler = lambda do
273
+ warn "\nCurrent: %s#%s %.2fs" % [self.class, self.name, Time.now - t0]
274
+ end
275
+
276
+ self.class.on_signal "INFO", handler, &block
277
+ end
278
+
279
+ include LifecycleHooks
280
+ include Guard
281
+ extend Guard
282
+ end # Test
283
+ end
@@ -0,0 +1,45 @@
1
+ # :stopdoc:
2
+
3
+ unless defined?(Betatest) then
4
+ # all of this crap is just to avoid circular requires and is only
5
+ # needed if a user requires "betatest/unit" directly instead of
6
+ # "betatest/autorun", so we also warn
7
+
8
+ from = caller.reject { |s| s =~ /rubygems/ }.join("\n ")
9
+ warn "Warning: you should require 'betatest/autorun' instead."
10
+ warn %(Warning: or add 'gem "betatest"' before 'require "betatest/autorun"')
11
+ warn "From:\n #{from}"
12
+
13
+ module Betatest; end
14
+ MiniTest = Betatest # prevents betatest.rb from requiring back to us
15
+ require "betatest"
16
+ end
17
+
18
+ MiniTest = Betatest unless defined?(MiniTest)
19
+
20
+ module Betatest
21
+ class Unit
22
+ VERSION = Betatest::VERSION
23
+ class TestCase < Betatest::Test
24
+ def self.inherited klass # :nodoc:
25
+ from = caller.first
26
+ warn "MiniTest::Unit::TestCase is now Betatest::Test. From #{from}"
27
+ super
28
+ end
29
+ end
30
+
31
+ def self.autorun # :nodoc:
32
+ from = caller.first
33
+ warn "MiniTest::Unit.autorun is now Betatest.autorun. From #{from}"
34
+ Betatest.autorun
35
+ end
36
+
37
+ def self.after_tests(&b)
38
+ from = caller.first
39
+ warn "MiniTest::Unit.after_tests is now Betatest.after_run. From #{from}"
40
+ Betatest.after_run(&b)
41
+ end
42
+ end
43
+ end
44
+
45
+ # :startdoc:
@@ -0,0 +1,3 @@
1
+ module Betatest
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,80 @@
1
+ require 'tempfile'
2
+ require 'stringio'
3
+ require 'betatest/autorun'
4
+
5
+ class Betatest::Test
6
+ def clean s
7
+ s.gsub(/^ {6}/, '')
8
+ end
9
+ end
10
+
11
+ class MetaMetaMetaTestCase < Betatest::Test
12
+ attr_accessor :reporter, :output, :tu
13
+
14
+ def run_tu_with_fresh_reporter flags = %w[--seed 42]
15
+ options = Betatest.process_args flags
16
+
17
+ @output = StringIO.new("")
18
+
19
+ self.reporter = Betatest::CompositeReporter.new
20
+ reporter << Betatest::SummaryReporter.new(@output, options)
21
+ reporter << Betatest::ProgressReporter.new(@output, options)
22
+
23
+ reporter.start
24
+
25
+ yield(reporter) if block_given?
26
+
27
+ @tus ||= [@tu]
28
+ @tus.each do |tu|
29
+ Betatest::Runnable.runnables.delete tu
30
+
31
+ tu.run reporter, options
32
+ end
33
+
34
+ reporter.report
35
+ end
36
+
37
+ def first_reporter
38
+ reporter.reporters.first
39
+ end
40
+
41
+ def assert_report expected, flags = %w[--seed 42], &block
42
+ header = clean <<-EOM
43
+ Run options: #{flags.map { |s| s =~ /\|/ ? s.inspect : s }.join " "}
44
+
45
+ # Running:
46
+
47
+ EOM
48
+
49
+ run_tu_with_fresh_reporter flags, &block
50
+
51
+ output = normalize_output @output.string.dup
52
+
53
+ assert_equal header + expected, output
54
+ end
55
+
56
+ def normalize_output output
57
+ output.sub!(/Finished in .*/, "Finished in 0.00")
58
+ output.sub!(/Loaded suite .*/, 'Loaded suite blah')
59
+
60
+ output.gsub!(/ = \d+.\d\d s = /, ' = 0.00 s = ')
61
+ output.gsub!(/0x[A-Fa-f0-9]+/, '0xXXX')
62
+
63
+ if windows? then
64
+ output.gsub!(/\[(?:[A-Za-z]:)?[^\]:]+:\d+\]/, '[FILE:LINE]')
65
+ output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in/, '\1FILE:LINE:in')
66
+ else
67
+ output.gsub!(/\[[^\]:]+:\d+\]/, '[FILE:LINE]')
68
+ output.gsub!(/^(\s+)[^:]+:\d+:in/, '\1FILE:LINE:in')
69
+ end
70
+
71
+ output
72
+ end
73
+
74
+ def setup
75
+ super
76
+ srand 42
77
+ Betatest::Test.reset
78
+ @tu = nil
79
+ end
80
+ end
@@ -0,0 +1,137 @@
1
+ require 'betatest/autorun'
2
+ require 'betatest/benchmark'
3
+
4
+ ##
5
+ # Used to verify data:
6
+ # http://www.wolframalpha.com/examples/RegressionAnalysis.html
7
+
8
+ class TestBetatestBenchmark < Betatest::Test
9
+ def test_cls_bench_exp
10
+ assert_equal [2, 4, 8, 16, 32], Betatest::Benchmark.bench_exp(2, 32, 2)
11
+ end
12
+
13
+ def test_cls_bench_linear
14
+ assert_equal [2, 4, 6, 8, 10], Betatest::Benchmark.bench_linear(2, 10, 2)
15
+ end
16
+
17
+ def test_cls_runnable_methods
18
+ assert_equal [], Betatest::Benchmark.runnable_methods
19
+
20
+ c = Class.new(Betatest::Benchmark) do
21
+ def bench_blah
22
+ end
23
+ end
24
+
25
+ assert_equal ["bench_blah"], c.runnable_methods
26
+ end
27
+
28
+ def test_cls_bench_range
29
+ assert_equal [1, 10, 100, 1_000, 10_000], Betatest::Benchmark.bench_range
30
+ end
31
+
32
+ def test_fit_exponential_clean
33
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
34
+ y = x.map { |n| 1.1 * Math.exp(2.1 * n) }
35
+
36
+ assert_fit :exponential, x, y, 1.0, 1.1, 2.1
37
+ end
38
+
39
+ def test_fit_exponential_noisy
40
+ x = [1.0, 1.9, 2.6, 3.4, 5.0]
41
+ y = [12, 10, 8.2, 6.9, 5.9]
42
+
43
+ # verified with Numbers and R
44
+ assert_fit :exponential, x, y, 0.95, 13.81148, -0.1820
45
+ end
46
+
47
+ def test_fit_logarithmic_clean
48
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
49
+ y = x.map { |n| 1.1 + 2.1 * Math.log(n) }
50
+
51
+ assert_fit :logarithmic, x, y, 1.0, 1.1, 2.1
52
+ end
53
+
54
+ def test_fit_logarithmic_noisy
55
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
56
+ # Generated with
57
+ # y = x.map { |n| jitter = 0.999 + 0.002 * rand; (Math.log(n) ) * jitter }
58
+ y = [0.0, 0.6935, 1.0995, 1.3873, 1.6097]
59
+
60
+ assert_fit :logarithmic, x, y, 0.95, 0, 1
61
+ end
62
+
63
+ def test_fit_constant_clean
64
+ x = (1..5).to_a
65
+ y = [5.0, 5.0, 5.0, 5.0, 5.0]
66
+
67
+ assert_fit :linear, x, y, nil, 5.0, 0
68
+ end
69
+
70
+ def test_fit_constant_noisy
71
+ x = (1..5).to_a
72
+ y = [1.0, 1.2, 1.0, 0.8, 1.0]
73
+
74
+ # verified in numbers and R
75
+ assert_fit :linear, x, y, nil, 1.12, -0.04
76
+ end
77
+
78
+ def test_fit_linear_clean
79
+ # y = m * x + b where m = 2.2, b = 3.1
80
+ x = (1..5).to_a
81
+ y = x.map { |n| 2.2 * n + 3.1 }
82
+
83
+ assert_fit :linear, x, y, 1.0, 3.1, 2.2
84
+ end
85
+
86
+ def test_fit_linear_noisy
87
+ x = [ 60, 61, 62, 63, 65]
88
+ y = [3.1, 3.6, 3.8, 4.0, 4.1]
89
+
90
+ # verified in numbers and R
91
+ assert_fit :linear, x, y, 0.8315, -7.9635, 0.1878
92
+ end
93
+
94
+ def test_fit_power_clean
95
+ # y = A x ** B, where B = b and A = e ** a
96
+ # if, A = 1, B = 2, then
97
+
98
+ x = [1.0, 2.0, 3.0, 4.0, 5.0]
99
+ y = [1.0, 4.0, 9.0, 16.0, 25.0]
100
+
101
+ assert_fit :power, x, y, 1.0, 1.0, 2.0
102
+ end
103
+
104
+ def test_fit_power_noisy
105
+ # from www.engr.uidaho.edu/thompson/courses/ME330/lecture/least_squares.html
106
+ x = [10, 12, 15, 17, 20, 22, 25, 27, 30, 32, 35]
107
+ y = [95, 105, 125, 141, 173, 200, 253, 298, 385, 459, 602]
108
+
109
+ # verified in numbers
110
+ assert_fit :power, x, y, 0.90, 2.6217, 1.4556
111
+
112
+ # income to % of households below income amount
113
+ # http://library.wolfram.com/infocenter/Conferences/6461/PowerLaws.nb
114
+ x = [15000, 25000, 35000, 50000, 75000, 100000]
115
+ y = [0.154, 0.283, 0.402, 0.55, 0.733, 0.843]
116
+
117
+ # verified in numbers
118
+ assert_fit :power, x, y, 0.96, 3.119e-5, 0.8959
119
+ end
120
+
121
+ def assert_fit msg, x, y, fit, exp_a, exp_b
122
+ bench = Betatest::Benchmark.new :blah
123
+
124
+ a, b, rr = bench.send "fit_#{msg}", x, y
125
+
126
+ assert_operator rr, :>=, fit if fit
127
+ assert_in_delta exp_a, a
128
+ assert_in_delta exp_b, b
129
+ end
130
+ end
131
+
132
+ describe "my class Bench" do
133
+ klass = self
134
+ it "should provide bench methods" do
135
+ klass.must_respond_to :bench
136
+ end
137
+ end