minitest 1.7.2 → 2.0.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.
- data/History.txt +27 -0
- data/Manifest.txt +6 -3
- data/README.txt +133 -67
- data/lib/minitest/benchmark.rb +324 -0
- data/lib/minitest/mock.rb +14 -7
- data/lib/minitest/pride.rb +35 -0
- data/lib/minitest/spec.rb +8 -5
- data/lib/minitest/unit.rb +179 -86
- data/test/test_minitest_benchmark.rb +98 -0
- data/test/test_minitest_mock.rb +106 -0
- data/test/{test_mini_spec.rb → test_minitest_spec.rb} +12 -2
- data/test/{test_mini_test.rb → test_minitest_unit.rb} +51 -73
- metadata +38 -18
- data/test/test_mini_mock.rb +0 -77
data/History.txt
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
=== 2.0.0 / 2010-11-11
|
2
|
+
|
3
|
+
* 3 major enhancements:
|
4
|
+
|
5
|
+
* Added minitest/benchmark! Assert your performance! YAY!
|
6
|
+
* Refactored runner to allow for more extensibility. See minitest/benchmark.
|
7
|
+
* This makes the runner backwards incompatible in some ways!
|
8
|
+
|
9
|
+
* 9 minor enhancements:
|
10
|
+
|
11
|
+
* Added MiniTest::Unit.after_tests { ... }
|
12
|
+
* Improved output by adding test rates and a more sortable verbose format
|
13
|
+
* Improved readme based on feedback from others
|
14
|
+
* Added io method to TestCase. If used, it'll supplant '.EF' output.
|
15
|
+
* Refactored IO in MiniTest::Unit.
|
16
|
+
* Refactored _run_anything to _run_suite to make it easier to wrap (ngauthier)
|
17
|
+
* Spec class names are now the unmunged descriptions (btakita)
|
18
|
+
* YAY for not having redundant rdoc/readmes!
|
19
|
+
* Help output is now generated from the flags you passed straight up.
|
20
|
+
|
21
|
+
* 4 bug fixes:
|
22
|
+
|
23
|
+
* Fixed scoping issue on minitest/mock (srbaker/prosperity)
|
24
|
+
* Fixed some of the assertion default messages
|
25
|
+
* Fixes autorun when on windows with ruby install on different drive (larsch)
|
26
|
+
* Fixed rdoc output bug in spec.rb
|
27
|
+
|
1
28
|
=== 1.7.2 / 2010-09-23
|
2
29
|
|
3
30
|
* 3 bug fixes:
|
data/Manifest.txt
CHANGED
@@ -5,9 +5,12 @@ README.txt
|
|
5
5
|
Rakefile
|
6
6
|
design_rationale.rb
|
7
7
|
lib/minitest/autorun.rb
|
8
|
+
lib/minitest/benchmark.rb
|
8
9
|
lib/minitest/mock.rb
|
10
|
+
lib/minitest/pride.rb
|
9
11
|
lib/minitest/spec.rb
|
10
12
|
lib/minitest/unit.rb
|
11
|
-
test/
|
12
|
-
test/
|
13
|
-
test/
|
13
|
+
test/test_minitest_benchmark.rb
|
14
|
+
test/test_minitest_mock.rb
|
15
|
+
test/test_minitest_spec.rb
|
16
|
+
test/test_minitest_unit.rb
|
data/README.txt
CHANGED
@@ -1,25 +1,44 @@
|
|
1
|
-
= minitest
|
1
|
+
= minitest/*
|
2
2
|
|
3
3
|
* http://rubyforge.org/projects/bfts
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
minitest
|
8
|
-
|
9
|
-
test writer and for language implementors that need a minimal set of
|
10
|
-
methods to bootstrap a working unit test suite.
|
7
|
+
minitest provides a complete suite of testing facilities supporting
|
8
|
+
TDD, BDD, mocking, and benchmarking.
|
11
9
|
|
12
|
-
|
10
|
+
minitest/unit is a small and incredibly fast unit testing framework.
|
11
|
+
It provides a rich set of assertions to make your tests clean and
|
12
|
+
readable.
|
13
13
|
|
14
|
-
|
14
|
+
minitest/spec is a functionally complete spec engine. It hooks onto
|
15
|
+
minitest/unit and seamlessly bridges test assertions over to spec
|
16
|
+
expectations.
|
15
17
|
|
16
|
-
|
18
|
+
minitest/benchmark is an awesome way to assert the performance of your
|
19
|
+
algorithms in a repeatable manner. Now you can assert that your newb
|
20
|
+
co-worker doesn't replace your linear algorithm with an exponential
|
21
|
+
one!
|
22
|
+
|
23
|
+
minitest/mock by Steven Baker, is a beautifully tiny mock object
|
24
|
+
framework.
|
25
|
+
|
26
|
+
minitest/pride shows pride in testing and adds coloring to your test
|
27
|
+
output.
|
28
|
+
|
29
|
+
minitest/unit is meant to have a clean implementation for language
|
30
|
+
implementors that need a minimal set of methods to bootstrap a working
|
31
|
+
test suite. For example, there is no magic involved for test-case
|
32
|
+
discovery.
|
17
33
|
|
18
34
|
== FEATURES/PROBLEMS:
|
19
35
|
|
20
|
-
*
|
21
|
-
*
|
22
|
-
*
|
36
|
+
* minitest/autorun - the easy and explicit way to run all your tests.
|
37
|
+
* minitest/unit - a very fast, simple, and clean test system.
|
38
|
+
* minitest/spec - a very fast, simple, and clean spec system.
|
39
|
+
* minitest/mock - a simple and clean mock system.
|
40
|
+
* minitest/benchmark - an awesome way to assert your algorithm's performance.
|
41
|
+
* minitest/pride - show your pride in testing!
|
23
42
|
* Incredibly small and fast runner, but no bells and whistles.
|
24
43
|
|
25
44
|
== RATIONALE:
|
@@ -30,100 +49,147 @@ See design_rationale.rb to see how specs and tests work in minitest.
|
|
30
49
|
|
31
50
|
Given that you'd like to test the following class:
|
32
51
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def does_it_blend?
|
39
|
-
"YES!"
|
40
|
-
end
|
52
|
+
class Meme
|
53
|
+
def i_can_has_cheezburger?
|
54
|
+
"OHAI!"
|
41
55
|
end
|
42
56
|
|
57
|
+
def does_it_blend?
|
58
|
+
"YES!"
|
59
|
+
end
|
60
|
+
end
|
43
61
|
|
44
62
|
=== Unit tests
|
45
63
|
|
46
|
-
|
47
|
-
MiniTest::Unit.autorun
|
64
|
+
require 'minitest/autorun'
|
48
65
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
66
|
+
class TestMeme < MiniTest::Unit::TestCase
|
67
|
+
def setup
|
68
|
+
@meme = Meme.new
|
69
|
+
end
|
53
70
|
|
54
|
-
|
55
|
-
|
56
|
-
|
71
|
+
def test_that_kitty_can_eat
|
72
|
+
assert_equal "OHAI!", @meme.i_can_has_cheezburger?
|
73
|
+
end
|
57
74
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
75
|
+
def test_that_it_doesnt_not_blend
|
76
|
+
refute_match /^no/i, @meme.does_it_blend?
|
61
77
|
end
|
78
|
+
end
|
62
79
|
|
63
80
|
=== Specs
|
64
81
|
|
65
|
-
|
66
|
-
|
82
|
+
require 'minitest/autorun'
|
83
|
+
|
84
|
+
describe Meme do
|
85
|
+
before do
|
86
|
+
@meme = Meme.new
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "when asked about cheeseburgers" do
|
90
|
+
it "must respond positively" do
|
91
|
+
@meme.i_can_has_cheezburger?.must_equal "OHAI!"
|
92
|
+
end
|
93
|
+
end
|
67
94
|
|
68
|
-
describe
|
69
|
-
|
70
|
-
@meme
|
95
|
+
describe "when asked about blending possibilities" do
|
96
|
+
it "won't say no" do
|
97
|
+
@meme.does_it_blend?.wont_match /^no/i
|
71
98
|
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
=== Benchmarks
|
103
|
+
|
104
|
+
Add benchmarks to your regular unit tests. If the unit tests fail, the
|
105
|
+
benchmarks won't run.
|
72
106
|
|
73
|
-
|
74
|
-
|
75
|
-
|
107
|
+
# optionally run benchmarks, good for CI-only work!
|
108
|
+
require 'minitest/benchmark' if ENV["BENCH"]
|
109
|
+
|
110
|
+
class TestMeme < MiniTest::Unit::TestCase
|
111
|
+
# Override self.bench_range or default range is [1, 10, 100, 1_000, 10_000]
|
112
|
+
def bench_my_algorithm
|
113
|
+
assert_performance_linear 0.9999 do |n| # n is a range value
|
114
|
+
n.times do
|
115
|
+
@obj.my_algorithm
|
76
116
|
end
|
77
117
|
end
|
118
|
+
end
|
119
|
+
end
|
78
120
|
|
79
|
-
|
80
|
-
|
81
|
-
|
121
|
+
Or add them to your specs. If you make benchmarks optional, you'll
|
122
|
+
need to wrap your benchmarks in a conditional since the methods won't
|
123
|
+
be defined.
|
124
|
+
|
125
|
+
describe Meme do
|
126
|
+
if ENV["BENCH"] then
|
127
|
+
bench_performance_linear "my_algorithm", 0.9999 do |n|
|
128
|
+
100.times do
|
129
|
+
@obj.my_algorithm(n)
|
82
130
|
end
|
83
131
|
end
|
84
132
|
end
|
133
|
+
end
|
134
|
+
|
135
|
+
outputs something like:
|
136
|
+
|
137
|
+
# Running benchmarks:
|
138
|
+
|
139
|
+
TestBlah 100 1000 10000
|
140
|
+
bench_my_algorithm 0.006167 0.079279 0.786993
|
141
|
+
bench_other_algorithm 0.061679 0.792797 7.869932
|
142
|
+
|
143
|
+
Output is tab-delimited to make it easy to paste into a spreadsheet.
|
85
144
|
|
86
145
|
=== Mocks
|
87
146
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
147
|
+
class MemeAsker
|
148
|
+
def initialize(meme)
|
149
|
+
@meme = meme
|
150
|
+
end
|
92
151
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
152
|
+
def ask(question)
|
153
|
+
method = question.tr(" ","_") + "?"
|
154
|
+
@meme.send(method)
|
97
155
|
end
|
156
|
+
end
|
98
157
|
|
99
|
-
|
100
|
-
require 'minitest/mock'
|
101
|
-
MiniTest::Unit.autorun
|
158
|
+
require 'minitest/autorun'
|
102
159
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
160
|
+
describe MemeAsker do
|
161
|
+
before do
|
162
|
+
@meme = MiniTest::Mock.new
|
163
|
+
@meme_asker = MemeAsker.new @meme
|
164
|
+
end
|
108
165
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
166
|
+
describe "#ask" do
|
167
|
+
describe "when passed an unpunctuated question" do
|
168
|
+
it "should invoke the appropriate predicate method on the meme" do
|
169
|
+
@meme.expect :does_it_blend?, :return_value
|
170
|
+
@meme_asker.ask "does it blend"
|
171
|
+
@meme.verify
|
116
172
|
end
|
117
173
|
end
|
118
174
|
end
|
175
|
+
end
|
119
176
|
|
120
177
|
== REQUIREMENTS:
|
121
178
|
|
122
|
-
|
179
|
+
* Ruby 1.8, maybe even 1.6 or lower. No magic is involved.
|
123
180
|
|
124
181
|
== INSTALL:
|
125
182
|
|
126
|
-
|
183
|
+
sudo gem install minitest
|
184
|
+
|
185
|
+
On 1.9, you already have it. To get newer candy you can still install
|
186
|
+
the gem, but you'll need to activate the gem explicitly to use it:
|
187
|
+
|
188
|
+
require 'rubygems'
|
189
|
+
gem 'minitest' # ensures you're using the gem, and not the built in MT
|
190
|
+
require 'minitest/autorun'
|
191
|
+
|
192
|
+
# ... usual testing stuffs ...
|
127
193
|
|
128
194
|
== LICENSE:
|
129
195
|
|
@@ -0,0 +1,324 @@
|
|
1
|
+
require 'minitest/unit'
|
2
|
+
require 'minitest/spec'
|
3
|
+
|
4
|
+
class MiniTest::Unit
|
5
|
+
attr_accessor :runner
|
6
|
+
|
7
|
+
def run_benchmarks
|
8
|
+
_run_anything :benchmark
|
9
|
+
end
|
10
|
+
|
11
|
+
def benchmark_suite_header suite
|
12
|
+
"\n#{suite}\t#{suite.bench_range.join("\t")}"
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestCase
|
16
|
+
##
|
17
|
+
# Returns a set of ranges stepped exponentially from +min+ to
|
18
|
+
# +max+ by powers of +base+. Eg:
|
19
|
+
#
|
20
|
+
# bench_exp(2, 16, 2) # => [2, 4, 8, 16]
|
21
|
+
|
22
|
+
def self.bench_exp min, max, base = 10
|
23
|
+
min = (Math.log10(min) / Math.log10(base)).to_i
|
24
|
+
max = (Math.log10(max) / Math.log10(base)).to_i
|
25
|
+
|
26
|
+
(min..max).map { |m| base ** m }.to_a
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Returns a set of ranges stepped linearly from +min+ to +max+ by
|
31
|
+
# +step+. Eg:
|
32
|
+
#
|
33
|
+
# bench_linear(20, 40, 10) # => [20, 30, 40]
|
34
|
+
|
35
|
+
def self.bench_linear min, max, step = 10
|
36
|
+
(min..max).step(step).to_a
|
37
|
+
rescue LocalJumpError # 1.8.6
|
38
|
+
r = []; (min..max).step(step) { |n| r << n }; r
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Returns the benchmark methods (methods that start with bench_)
|
43
|
+
# for that class.
|
44
|
+
|
45
|
+
def self.benchmark_methods # :nodoc:
|
46
|
+
public_instance_methods(true).grep(/^bench_/).map { |m| m.to_s }.sort
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Returns all test suites that have benchmark methods.
|
51
|
+
|
52
|
+
def self.benchmark_suites
|
53
|
+
TestCase.test_suites.reject { |s| s.benchmark_methods.empty? }
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Specifies the ranges used for benchmarking for that class.
|
58
|
+
# Defaults to exponential growth from 1 to 10k by powers of 10.
|
59
|
+
# Override if you need different ranges for your benchmarks.
|
60
|
+
#
|
61
|
+
# See also: ::bench_exp and ::bench_linear.
|
62
|
+
|
63
|
+
def self.bench_range
|
64
|
+
bench_exp 1, 10_000
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Runs the given +work+, gathering the times of each run. Range
|
69
|
+
# and times are then passed to a given +validation+ proc. Outputs
|
70
|
+
# the benchmark name and times in tab-separated format, making it
|
71
|
+
# easy to paste into a spreadsheet for graphing or further
|
72
|
+
# analysis.
|
73
|
+
#
|
74
|
+
# Ranges are specified by ::bench_range.
|
75
|
+
#
|
76
|
+
# Eg:
|
77
|
+
#
|
78
|
+
# def bench_algorithm
|
79
|
+
# validation = proc { |x, y| ... }
|
80
|
+
# assert_performance validation do |x|
|
81
|
+
# @obj.algorithm
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
|
85
|
+
def assert_performance validation, &work
|
86
|
+
range = self.class.bench_range
|
87
|
+
|
88
|
+
io.print "#{__name__}"
|
89
|
+
|
90
|
+
times = []
|
91
|
+
|
92
|
+
range.each do |x|
|
93
|
+
GC.start
|
94
|
+
t0 = Time.now
|
95
|
+
instance_exec(x, &work)
|
96
|
+
t = Time.now - t0
|
97
|
+
|
98
|
+
io.print "\t%9.6f" % t
|
99
|
+
times << t
|
100
|
+
end
|
101
|
+
io.puts
|
102
|
+
|
103
|
+
validation[range, times]
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Runs the given +work+ and asserts that the times gathered fit to
|
108
|
+
# match a constant rate (eg, linear slope == 0) within a given error
|
109
|
+
# +threshold+.
|
110
|
+
#
|
111
|
+
# Fit is calculated by #fit_constant.
|
112
|
+
#
|
113
|
+
# Ranges are specified by ::bench_range.
|
114
|
+
#
|
115
|
+
# Eg:
|
116
|
+
#
|
117
|
+
# def bench_algorithm
|
118
|
+
# assert_performance_constant 0.9999 do |x|
|
119
|
+
# @obj.algorithm
|
120
|
+
# end
|
121
|
+
# end
|
122
|
+
|
123
|
+
def assert_performance_constant threshold = 0.99, &work
|
124
|
+
validation = proc do |range, times|
|
125
|
+
a, b, rr = fit_linear range, times
|
126
|
+
assert_in_delta 0, b, 1 - threshold
|
127
|
+
[a, b, rr]
|
128
|
+
end
|
129
|
+
|
130
|
+
assert_performance validation, &work
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Runs the given +work+ and asserts that the times gathered fit to
|
135
|
+
# match a exponential curve within a given error +threshold+.
|
136
|
+
#
|
137
|
+
# Fit is calculated by #fit_exponential.
|
138
|
+
#
|
139
|
+
# Ranges are specified by ::bench_range.
|
140
|
+
#
|
141
|
+
# Eg:
|
142
|
+
#
|
143
|
+
# def bench_algorithm
|
144
|
+
# assert_performance_exponential 0.9999 do |x|
|
145
|
+
# @obj.algorithm
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
|
149
|
+
def assert_performance_exponential threshold = 0.99, &work
|
150
|
+
assert_performance validation_for_fit(:exponential, threshold), &work
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# Runs the given +work+ and asserts that the times gathered fit to
|
155
|
+
# match a straight line within a given error +threshold+.
|
156
|
+
#
|
157
|
+
# Fit is calculated by #fit_linear.
|
158
|
+
#
|
159
|
+
# Ranges are specified by ::bench_range.
|
160
|
+
#
|
161
|
+
# Eg:
|
162
|
+
#
|
163
|
+
# def bench_algorithm
|
164
|
+
# assert_performance_linear 0.9999 do |x|
|
165
|
+
# @obj.algorithm
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
|
169
|
+
def assert_performance_linear threshold = 0.99, &work
|
170
|
+
assert_performance validation_for_fit(:linear, threshold), &work
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Runs the given +work+ and asserts that the times gathered curve
|
175
|
+
# fit to match a power curve within a given error +threshold+.
|
176
|
+
#
|
177
|
+
# Fit is calculated by #fit_power.
|
178
|
+
#
|
179
|
+
# Ranges are specified by ::bench_range.
|
180
|
+
#
|
181
|
+
# Eg:
|
182
|
+
#
|
183
|
+
# def bench_algorithm
|
184
|
+
# assert_performance_power 0.9999 do |x|
|
185
|
+
# @obj.algorithm
|
186
|
+
# end
|
187
|
+
# end
|
188
|
+
|
189
|
+
def assert_performance_power threshold = 0.99, &work
|
190
|
+
assert_performance validation_for_fit(:power, threshold), &work
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# Takes an array of x/y pairs and calculates the general R^2 value.
|
195
|
+
#
|
196
|
+
# See: http://en.wikipedia.org/wiki/Coefficient_of_determination
|
197
|
+
|
198
|
+
def fit_error xys
|
199
|
+
y_bar = sigma(xys) { |x, y| y } / xys.size.to_f
|
200
|
+
ss_tot = sigma(xys) { |x, y| (y - y_bar) ** 2 }
|
201
|
+
ss_err = sigma(xys) { |x, y| (yield(x) - y) ** 2 }
|
202
|
+
|
203
|
+
1 - (ss_err / ss_tot)
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# To fit a functional form: y = ae^(bx).
|
208
|
+
#
|
209
|
+
# Takes x and y values and returns [a, b, r^2].
|
210
|
+
#
|
211
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
|
212
|
+
|
213
|
+
def fit_exponential xs, ys
|
214
|
+
n = xs.size
|
215
|
+
xys = xs.zip(ys)
|
216
|
+
sxlny = sigma(xys) { |x,y| x * Math.log(y) }
|
217
|
+
slny = sigma(xys) { |x,y| Math.log(y) }
|
218
|
+
sx2 = sigma(xys) { |x,y| x * x }
|
219
|
+
sx = sigma xs
|
220
|
+
|
221
|
+
c = n * sx2 - sx ** 2
|
222
|
+
a = (slny * sx2 - sx * sxlny) / c
|
223
|
+
b = ( n * sxlny - sx * slny ) / c
|
224
|
+
|
225
|
+
return Math.exp(a), b, fit_error(xys) { |x| Math.exp(a + b * x) }
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# Fits the functional form: a + bx.
|
230
|
+
#
|
231
|
+
# Takes x and y values and returns [a, b, r^2].
|
232
|
+
#
|
233
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFitting.html
|
234
|
+
|
235
|
+
def fit_linear xs, ys
|
236
|
+
n = xs.size
|
237
|
+
xys = xs.zip(ys)
|
238
|
+
sx = sigma xs
|
239
|
+
sy = sigma ys
|
240
|
+
sx2 = sigma(xs) { |x| x ** 2 }
|
241
|
+
sxy = sigma(xys) { |x,y| x * y }
|
242
|
+
|
243
|
+
c = n * sx2 - sx**2
|
244
|
+
a = (sy * sx2 - sx * sxy) / c
|
245
|
+
b = ( n * sxy - sx * sy ) / c
|
246
|
+
|
247
|
+
return a, b, fit_error(xys) { |x| a + b * x }
|
248
|
+
end
|
249
|
+
|
250
|
+
##
|
251
|
+
# To fit a functional form: y = ax^b.
|
252
|
+
#
|
253
|
+
# Takes x and y values and returns [a, b, r^2].
|
254
|
+
#
|
255
|
+
# See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
256
|
+
|
257
|
+
def fit_power xs, ys
|
258
|
+
n = xs.size
|
259
|
+
xys = xs.zip(ys)
|
260
|
+
slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) }
|
261
|
+
slnx = sigma(xs) { |x | Math.log(x) }
|
262
|
+
slny = sigma(ys) { | y| Math.log(y) }
|
263
|
+
slnx2 = sigma(xs) { |x | Math.log(x) ** 2 }
|
264
|
+
|
265
|
+
b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2);
|
266
|
+
a = (slny - b * slnx) / n
|
267
|
+
|
268
|
+
return Math.exp(a), b, fit_error(xys) { |x| (Math.exp(a) * (x ** b)) }
|
269
|
+
end
|
270
|
+
|
271
|
+
##
|
272
|
+
# Enumerates over +enum+ mapping +block+ if given, returning the
|
273
|
+
# sum of the result. Eg:
|
274
|
+
#
|
275
|
+
# sigma([1, 2, 3]) # => 1 + 2 + 3 => 7
|
276
|
+
# sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
|
277
|
+
|
278
|
+
def sigma enum, &block
|
279
|
+
enum = enum.map(&block) if block
|
280
|
+
enum.inject { |sum, n| sum + n }
|
281
|
+
end
|
282
|
+
|
283
|
+
##
|
284
|
+
# Returns a proc that calls the specified fit method and asserts
|
285
|
+
# that the error is within a tolerable threshold.
|
286
|
+
|
287
|
+
def validation_for_fit msg, threshold
|
288
|
+
proc do |range, times|
|
289
|
+
a, b, rr = send "fit_#{msg}", range, times
|
290
|
+
assert_operator rr, :>=, threshold
|
291
|
+
[a, b, rr]
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
class MiniTest::Spec
|
298
|
+
def self.bench name, &block
|
299
|
+
define_method "bench_#{name.gsub(/\W+/, '_')}", &block
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.bench_range &block
|
303
|
+
meta = (class << self; self; end)
|
304
|
+
meta.send :define_method, "bench_range", &block
|
305
|
+
end
|
306
|
+
|
307
|
+
def self.bench_performance_linear name, threshold = 0.9, &work
|
308
|
+
bench name do
|
309
|
+
assert_performance_linear threshold, &work
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
def self.bench_performance_constant name, threshold = 0.99, &work
|
314
|
+
bench name do
|
315
|
+
assert_performance_constant threshold, &work
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def self.bench_performance_exponential name, threshold = 0.99, &work
|
320
|
+
bench name do
|
321
|
+
assert_performance_exponential threshold, &work
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|