betatest 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,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