b 1.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.
@@ -0,0 +1,18 @@
1
+ *.swp
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in b.gemspec
4
+ gemspec
@@ -0,0 +1,6 @@
1
+ guard 'rspec', :cli => '--color --format doc --fail-fast' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
6
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 moe@busyloop.net
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,82 @@
1
+ # B::enchmark [![Build Status](https://travis-ci.org/busyloop/b.png?branch=master)](https://travis-ci.org/busyloop/b) [![Dependency Status](https://gemnasium.com/busyloop/b.png)](https://gemnasium.com/busyloop/b)
2
+
3
+ A small, convenient benchmark-library.
4
+
5
+ ## Features
6
+
7
+ * Sensible defaults and default-output
8
+
9
+ * Displays relative performance; "B was 1.4x slower than A"
10
+
11
+ * Returns benchmark-results as Array of Hashes (for easy integration with your unit-tests or CI)
12
+
13
+ * Output can be customized with a simple Plugin-API (ships with plugins for TSV and HTML)
14
+
15
+ * High precision (via [hitimes](https://github.com/copiousfreetime/hitimes))
16
+
17
+
18
+ ## Installation
19
+
20
+ `gem install b`
21
+
22
+ ## Example
23
+
24
+ ```ruby
25
+ #!/usr/bin/env ruby
26
+
27
+ require 'b'
28
+
29
+ B.enchmark('Sleep "performance"', :rounds => 10, :compare => :mean) do
30
+ job '300ms' do
31
+ sleep 0.3
32
+ end
33
+
34
+ job '100ms' do
35
+ sleep 0.1
36
+ end
37
+
38
+ job '200ms' do
39
+ sleep 0.2
40
+ end
41
+ end
42
+ ```
43
+
44
+ ### Output:
45
+
46
+ ```
47
+ -- Sleep "performance" ---------------------------------------------------------------
48
+ rounds r/s mean max min ± stddev x mean
49
+ 100ms 10 9.99 100.13 100.17 100.11 0.02 1.00
50
+ 200ms 10 5.00 200.14 200.17 200.11 0.02 2.00
51
+ 300ms 10 3.33 300.13 300.18 300.11 0.02 3.00
52
+ ```
53
+
54
+ ## Getting started
55
+
56
+ For advanced usage beyond the above example please look at the included demos:
57
+
58
+ 1. Run `ruby -r b/demo -e0`
59
+ 2. [demo.rb](https://github.com/busyloop/b/blob/master/lib/b/demo.rb)
60
+
61
+ ## License (MIT)
62
+
63
+ Copyright (c) 2013 moe@busyloop.net
64
+
65
+ Permission is hereby granted, free of charge, to any person obtaining
66
+ a copy of this software and associated documentation files (the
67
+ "Software"), to deal in the Software without restriction, including
68
+ without limitation the rights to use, copy, modify, merge, publish,
69
+ distribute, sublicense, and/or sell copies of the Software, and to
70
+ permit persons to whom the Software is furnished to do so, subject to
71
+ the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be
74
+ included in all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
77
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
79
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
80
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
81
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
82
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ task :default => :test
6
+
7
+ RSpec::Core::RakeTask.new("test:spec") do |t|
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ t.rspec_opts = '--fail-fast -b -c -f documentation --tag ~benchmark'
10
+ end
11
+
12
+ desc 'Run full test suite'
13
+ task :test => [ 'test:spec' ]
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'b/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "b"
8
+ gem.version = B::VERSION
9
+ gem.authors = ["Moe"]
10
+ gem.email = ["moe@busyloop.net"]
11
+ gem.description = %q{A small, convenient benchmark-library.}
12
+ gem.summary = %q{A small, convenient benchmark-library.}
13
+ gem.homepage = "https://github.com/busyloop/b"
14
+ gem.has_rdoc = false
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency 'simplecov'
22
+ gem.add_development_dependency 'rake'
23
+ gem.add_development_dependency 'rspec'
24
+ gem.add_development_dependency 'guard'
25
+ gem.add_development_dependency 'guard-rspec'
26
+ gem.add_development_dependency 'stdiotrap'
27
+ gem.add_dependency 'hitimes'
28
+ gem.add_dependency 'blockenspiel'
29
+ end
@@ -0,0 +1,128 @@
1
+ require "b/version"
2
+ require "b/output_plugins"
3
+ require "hitimes"
4
+ require "blockenspiel"
5
+
6
+ module B
7
+ class SanityViolation < RuntimeError; end
8
+
9
+ DEFAULT_ROUNDS = 50
10
+
11
+ def self.enchmark(id='Untitled Benchmark', opts={}, &block)
12
+ opts = {:output => ConsoleWriter.new}.merge(opts)
13
+ opts[:parent] = opts[:output]
14
+ Blockenspiel.invoke(block, Enchmark.new(id, opts)).run!
15
+ end
16
+
17
+ # B::Ase
18
+ class Ase
19
+ include Blockenspiel::DSL
20
+ attr_reader :opts
21
+
22
+ dsl_methods false
23
+
24
+ def run!
25
+ @children.map {|c| c.run! unless c.nil?} unless @children.nil?
26
+ post_run if self.respond_to? :post_run
27
+ @children.map(&:to_h)
28
+ end
29
+
30
+ def register(job)
31
+ @parent.register(job) if @parent.respond_to? :register
32
+ end
33
+
34
+ def start(job)
35
+ @parent.start(job) if @parent.respond_to? :start
36
+ end
37
+
38
+ def finish(job)
39
+ @parent.finish(job) if @parent.respond_to? :finish
40
+ end
41
+ end
42
+
43
+ # B::Enchmark
44
+ class Enchmark < B::Ase
45
+ attr_reader :id
46
+
47
+ def job(id, opts={}, &block)
48
+ if opts[:rounds] and @compare
49
+ raise SanityViolation, "Can not override :rounds on job '#{id}' when comparing on :#{@compare}!"
50
+ end
51
+ if opts[:compare]
52
+ raise SanityViolation, "Can not override :compare in a job ('#{id}')"
53
+ end
54
+ opts = @opts.merge(opts)
55
+ (@children ||= []) << Job.new(self, id, opts, block)
56
+ self
57
+ end
58
+
59
+ dsl_methods false
60
+ def initialize(id, opts={})
61
+ @opts = {:rounds => DEFAULT_ROUNDS, :compare => nil}.merge(opts)
62
+ @parent = opts[:parent]
63
+ @id, @rounds, @compare = id, opts[:rounds], opts[:compare]
64
+ @buffer = []
65
+ end
66
+
67
+ def post_run
68
+ if @compare
69
+ @buffer.sort! do |a,b|
70
+ a.send(@compare) <=> b.send(@compare)
71
+ end
72
+ end
73
+ @buffer.each_with_index do |job, i|
74
+ if 0 == i
75
+ job.x = 1.0
76
+ else
77
+ job.x = job.send(@compare) / @buffer[0].send(@compare)
78
+ end
79
+ end
80
+ until @buffer.empty? do
81
+ @parent.start @buffer[0]
82
+ @parent.finish @buffer.shift
83
+ end
84
+ end
85
+
86
+ def start(job)
87
+ @compare.nil? ? super : @buffer << job
88
+ end
89
+
90
+ def finish(job)
91
+ super if @compare.nil?
92
+ end
93
+
94
+ # B::Enchmark::Job
95
+ class Job < B::Ase
96
+ ATTRIBUTES = [:group, :id, :rounds, :rate, :mean, :max, :min, :stddev, :x, :compare]
97
+ attr_reader *ATTRIBUTES
98
+ attr_writer :x
99
+
100
+ dsl_methods false
101
+ def initialize(parent, id, opts, block)
102
+ @opts = parent.opts.merge(opts)
103
+ @parent, @block, @group, @id, @rounds, @compare = parent, block, parent.id, id, @opts[:rounds], @opts[:compare]
104
+ @parent.register(self)
105
+ end
106
+
107
+ def run!
108
+ tm = Hitimes::TimedMetric.new(nil)
109
+ @parent.start self
110
+ @opts[:rounds].times do
111
+ tm.start
112
+ @block.call
113
+ tm.stop
114
+ end
115
+ @rate, @mean, @max, @min, @stddev = tm.rate, tm.mean, tm.max, tm.min, tm.stddev
116
+ finish self
117
+ end
118
+
119
+ def to_h
120
+ ATTRIBUTES.reduce({}) { |m,e|
121
+ m[e] = instance_variable_get('@'+e.to_s)
122
+ m
123
+ }
124
+ end
125
+ end
126
+ end
127
+ end
128
+
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'b'
4
+
5
+ puts "Source-code is here: #{__FILE__}"
6
+ puts
7
+
8
+ # Basic sleep benchmark
9
+ results = B.enchmark("Nap time; output in milliseconds", :rounds => 3) do
10
+ job('sleep 0.1', :rounds => 10) { sleep 0.1 }
11
+ job('sleep 0.5') { sleep 0.5 }
12
+ job('sleep 1.0') { sleep 1.0 }
13
+ end
14
+
15
+ # Results are also returned as an Array of Hashes
16
+ #puts results
17
+
18
+ puts
19
+
20
+ # Same as above but display results in seconds instead of ms.
21
+ # HINT: You may also set :writer => nil if you want no output at all.
22
+ cw = B::ConsoleWriter.new({:multiply=>1, :round=>6})
23
+ result B.enchmark("Nap time; output in seconds", :output => cw, :rounds => 3) do
24
+ job('sleep 0.1', :rounds => 10) { sleep 0.1 }
25
+ job('sleep 0.5') { sleep 0.5 }
26
+ job('sleep 1.0') { sleep 1.0 }
27
+ end
28
+
29
+ #puts results
30
+ puts
31
+
32
+ # Fibonacci snippets taken from http://stackoverflow.com/questions/6418524/fibonacci-one-liner
33
+ B.enchmark("Compare fibonacci implementations", :rounds => 500, :compare => :mean) do
34
+ job 'fibonacci lambda A' do
35
+ f = lambda { |x| x < 2 ? x : f.call(x-1) + f.call(x-2) }
36
+ (0..16).each { |i| f.call(i) }
37
+ end
38
+
39
+ job 'fibonacci lambda B' do
40
+ f = lambda { |n| (0..n).inject([1,0]) { |(a,b), _| [b, a+b] }[0] }
41
+ (0..16).each { |i| f.call(i) }
42
+ end
43
+
44
+ job 'fibonacci hash' do
45
+ f = Hash.new{ |h,k| h[k] = k < 2 ? k : h[k-1] + h[k-2] }
46
+ (0..16).each { |i| f[i] }
47
+ end
48
+ end
49
+
50
+ puts
51
+
52
+ B.enchmark('Compare string-concat methods (50k per round)', :rounds => 10, :compare => :mean) do
53
+ job 'x = a << b << c' do
54
+ 50_000.times do
55
+ a, b, c = 'a', 'b', 'c'
56
+ x = a << b << c
57
+ end
58
+ end
59
+
60
+ job 'x = "#{a}#{b}#{c}"' do
61
+ 50_000.times do
62
+ a, b, c = 'a', 'b', 'c'
63
+ x = "#{a}#{b}#{c}"
64
+ end
65
+ end
66
+
67
+ job 'x = a+b+c' do
68
+ 50_000.times do
69
+ a, b, c = 'a', 'b', 'c'
70
+ x = a+b+c
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,127 @@
1
+ module B
2
+ # print results in TSV format
3
+ class TsvWriter
4
+ COLUMNS = [:group, :id, :rounds, :rate, :mean, :max, :min, :stddev, :x]
5
+ def initialize(out=$stdout)
6
+ @out = out
7
+ printf COLUMNS.join("\t") + "\n"
8
+ end
9
+
10
+ def finish(job)
11
+ printf COLUMNS.map { |c| job.send(c) }.join("\t") + "\n"
12
+ end
13
+
14
+ private
15
+ def printf(*a)
16
+ @out.printf *a
17
+ end
18
+ end
19
+
20
+ # print results as a HTML table
21
+ class HtmlWriter
22
+ COLUMNS = [:group, :id, :rounds, :rate, :mean, :max, :min, :stddev, :x]
23
+
24
+ def register(job)
25
+ @todo = (@todo||0) +1
26
+ end
27
+
28
+ def initialize(out=$stdout)
29
+ @out = out
30
+ printf "<table>\n<tr>"
31
+ COLUMNS.map { |c| printf "<th>#{c}</th>" }
32
+ printf "</tr>\n"
33
+ end
34
+
35
+ def finish(job)
36
+ @done = (@done||0) +1
37
+ printf "<tr>"
38
+ COLUMNS.map { |c| printf "<td>#{job.send(c)}</td>" }
39
+ printf "</tr>\n"
40
+
41
+ printf "</table>\n" if @done == @todo
42
+ end
43
+
44
+ private
45
+ def printf(*a)
46
+ @out.printf *a
47
+ end
48
+ end
49
+
50
+ # print results in human friendly tabular format
51
+ #
52
+ # usage hints:
53
+ # * set :multiply=>1 if you want output in seconds instead of milliseconds
54
+ # * increase :column_width if you see indendation issues with wide values
55
+ # * increase :max_label_width if you want to see more of your labels
56
+ #
57
+ class ConsoleWriter
58
+ C_ID, C_LABEL, C_WIDTH, C_ROUND, C_MUL = *(0..4)
59
+
60
+ def initialize(opts={})
61
+ @opts = opts = { :out => $stderr, :multiply => 1000, :round => 2,
62
+ :column_width => 11, :max_label_width => 20 }.merge(opts)
63
+
64
+ @out = opts[:out]
65
+ @max_label_width = opts[:max_label_width]
66
+
67
+ @columns = [
68
+ # C_ID C_LABEL C_WIDTH C_ROUND C_MUL
69
+ [:id, '', -1, nil, nil],
70
+ [:rounds, 'rounds', -1, 0, 0],
71
+ [:rate, 'r/s', opts[:column_width], opts[:round], 1],
72
+ [:mean, 'mean', opts[:column_width], opts[:round], opts[:multiply]],
73
+ [:max, 'max', opts[:column_width], opts[:round], opts[:multiply]],
74
+ [:min, 'min', opts[:column_width], opts[:round], opts[:multiply]],
75
+ [:stddev, "\u00b1 stddev", opts[:column_width], opts[:round], opts[:multiply]]
76
+ ]
77
+
78
+ end
79
+
80
+ def register(job)
81
+ # adjust width of first column (label) to fit longest label
82
+ @columns[0][C_WIDTH] = [@columns[0][C_WIDTH], [job.id.length+2,@max_label_width].min].max
83
+ # adjust width of second column (rounds) to fit widest value
84
+ @columns[1][C_WIDTH] = [@columns[1][C_WIDTH], @columns[1][C_LABEL].length, job.rounds.to_s.length].max
85
+ end
86
+
87
+ def start(job)
88
+ if @header_printed.nil?
89
+ @header_printed = true
90
+
91
+ # add :x column if this is a comparison
92
+ if job.compare
93
+ @columns << [:x, "x #{job.compare}", @opts[:column_width], @opts[:round], 1]
94
+ end
95
+
96
+ # print header
97
+ header = '-' * (@columns.transpose[C_WIDTH].reduce(&:+) + @columns.length - 1)
98
+ header[2..3+job.group.length] = " #{job.group} "
99
+ printf header + "\n"
100
+ @columns.each_with_index do |col, i|
101
+ printf col[C_LABEL].rjust(col[C_WIDTH]) + ' '
102
+ end
103
+ printf "\n"
104
+ end
105
+ # print job.label
106
+ printf "#{job.id[0..@columns[0][C_WIDTH]-1].ljust(@columns[0][C_WIDTH])} "
107
+ # print rounds
108
+ printf "%#{@columns[1][C_WIDTH]}d ", job.send(:rounds)
109
+ end
110
+
111
+ def finish(job)
112
+ @columns.each_with_index do |col, i|
113
+ next if 2 > i # first two columns were already printed in start()
114
+ value = job.send(col[C_ID]) * col[C_MUL]
115
+
116
+ printf "%#{col[C_WIDTH]}.#{col[C_ROUND]}f ", value
117
+ end
118
+ printf "\n"
119
+ end
120
+
121
+ private
122
+ def printf(*a)
123
+ @opts[:out].printf *a
124
+ end
125
+ end
126
+ end
127
+
@@ -0,0 +1,3 @@
1
+ module B
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,244 @@
1
+ require 'spec_helper'
2
+
3
+ describe B do
4
+ describe :enchmark do
5
+ before (:each) do
6
+ StdioTrap.trap!
7
+ end
8
+
9
+ after (:each) do
10
+ StdioTrap.release!
11
+ end
12
+
13
+ describe 'with opts={}' do
14
+ let(:test_opts) { {} }
15
+ describe :job do
16
+ it 'raises SanityViolation when trying to override :compare' do
17
+ lambda {
18
+ B.enchmark do
19
+ job('a', {:compare => :min}) {}
20
+ end
21
+ }.should raise_error B::SanityViolation
22
+ end
23
+
24
+ it 'is called B::DEFAULT_ROUNDS times by default' do
25
+ a = mock(:a)
26
+ a.should_receive(:call).exactly(B::DEFAULT_ROUNDS).times
27
+ B.enchmark do
28
+ job('a') { a.call }
29
+ end
30
+ end
31
+
32
+ it 'can override :rounds' do
33
+ a, b, c, d = mock(:a), mock(:b), mock(:c), mock(:d)
34
+ a.should_receive(:call).exactly(B::DEFAULT_ROUNDS).times
35
+ b.should_receive(:call).exactly(5).times
36
+ c.should_receive(:call).exactly(B::DEFAULT_ROUNDS).times
37
+ d.should_receive(:call).exactly(10).times
38
+ B.enchmark do
39
+ job('a') { a.call }
40
+ job('b', :rounds => 5) { b.call }
41
+ job('c') { c.call }
42
+ job('d', :rounds => 10) { d.call }
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "return value" do
48
+ it "is an Array of well-formed Hashes" do
49
+ results = B.enchmark do
50
+ job('a') {}
51
+ job('b') {}
52
+ end
53
+ results.length.should == 2
54
+ results.each do |h|
55
+ h.should be_a Hash
56
+ B::Enchmark::Job::ATTRIBUTES.each do |a|
57
+ h.should have_key a
58
+ end
59
+ end
60
+ end
61
+
62
+ it "retval[*][:x] is nil" do
63
+ results = B.enchmark do
64
+ job('a') {}
65
+ job('b') {}
66
+ end
67
+ results.each do |h|
68
+ B::Enchmark::Job::ATTRIBUTES.each do |a|
69
+ h[:x].should == nil
70
+ end
71
+ end
72
+ end
73
+
74
+ it "looks about correct (timing)" do
75
+ results = B.enchmark('', test_opts) do
76
+ job('a', :rounds => 2 ) { sleep 0.5 }
77
+ job('b', :rounds => 1 ) { sleep 1 }
78
+ end
79
+ results.length.should == 2
80
+
81
+ results[0][:rounds].should == 2
82
+ results[0][:mean].should be >= 0.5
83
+ results[0][:mean].should be <= 1.0
84
+
85
+ results[1][:rounds].should == 1
86
+ results[1][:mean].should be >= 1.0
87
+ results[1][:mean].should be <= 1.5
88
+ end
89
+ end
90
+
91
+ describe "stdout" do
92
+ it "is empty" do
93
+ B.enchmark do
94
+ job('a') {}
95
+ end
96
+ StdioTrap.stdout.should == ''
97
+ end
98
+ end
99
+
100
+ describe "stderr" do
101
+ it "looks like output from the default ConsoleWriter" do
102
+ B.enchmark do
103
+ job('a') {}
104
+ end
105
+ StdioTrap.stderr.should match /^-- Untitled Benchmark/
106
+ StdioTrap.stderr.should match /rounds/
107
+ StdioTrap.stderr.should match /stddev/
108
+ StdioTrap.stderr.lines.count.should == 3
109
+ end
110
+ end
111
+ end
112
+
113
+ describe 'with opts={:compare => :mean, :rounds => 1}' do
114
+ let(:test_opts) { {:compare => :mean, :rounds => 1} }
115
+
116
+ describe :job do
117
+ it 'raises SanityViolation when trying to override :compare' do
118
+ lambda {
119
+ B.enchmark('', test_opts) do
120
+ job('a', {:compare => :min}) {}
121
+ end
122
+ }.should raise_error B::SanityViolation
123
+ end
124
+
125
+ it 'raises SanityViolation when trying to override :rounds' do
126
+ lambda {
127
+ B.enchmark('', test_opts) do
128
+ job('a', :rounds => 2) {}
129
+ end
130
+ }.should raise_error B::SanityViolation
131
+ end
132
+ end
133
+
134
+ describe "return value" do
135
+ it "is an Array of well-formed Hashes" do
136
+ results = B.enchmark('', test_opts) do
137
+ job('a') {}
138
+ job('b') {}
139
+ end
140
+
141
+ results.length.should == 2
142
+ results.each do |h|
143
+ h.should be_a Hash
144
+ B::Enchmark::Job::ATTRIBUTES.each do |a|
145
+ h.should have_key a
146
+ end
147
+ end
148
+ end
149
+
150
+ it "retval[*][:x] is not nil" do
151
+ results = B.enchmark('', test_opts) do
152
+ job('a') {}
153
+ job('b') {}
154
+ end
155
+ results.each do |h|
156
+ B::Enchmark::Job::ATTRIBUTES.each do |a|
157
+ h.should_not == nil
158
+ end
159
+ end
160
+ end
161
+
162
+ it "is sorted on :mean (and :x)" do
163
+ results = B.enchmark('', test_opts) do
164
+ job('b') { sleep 0.3 }
165
+ job('a') { sleep 0.1 }
166
+ job('d') { sleep 0.9 }
167
+ job('c') { sleep 0.6 }
168
+ end
169
+ as_returned = results.collect {|e| [e[:mean], e[:id]]}
170
+ sorted = as_returned.sort_by {|e| e[0]}
171
+ sorted.collect {|e| e[1]}.should == ['a', 'b', 'c', 'd']
172
+
173
+ as_returned = results.collect {|e| [e[:x], e[:id]]}
174
+ sorted = as_returned.sort_by {|e| e[0]}
175
+ sorted.collect {|e| e[1]}.should == ['a', 'b', 'c', 'd']
176
+ end
177
+
178
+ end
179
+ end
180
+
181
+ describe 'with opts={:output => nil}' do
182
+ let(:test_opts) { { :output=> nil } }
183
+
184
+ it 'doesn\'t write to stdout nor stderr' do
185
+ B.enchmark('', test_opts) do
186
+ job('a') {}
187
+ end
188
+ StdioTrap.stdout.should == ''
189
+ StdioTrap.stderr.should == ''
190
+ end
191
+ end
192
+ end
193
+
194
+ describe 'TsvWriter' do
195
+ before (:each) do
196
+ StdioTrap.trap!
197
+ output = B::TsvWriter.new
198
+ B.enchmark('foobar', :output => output) do
199
+ job('a') {}
200
+ end
201
+ @stdio = StdioTrap.release!
202
+ end
203
+
204
+ describe "stdout" do
205
+ it "looks like output from TsvWriter" do
206
+ @stdio[:stdout].should match /^group\tid\trounds\trate\tmean\tmax\tmin\tstddev\tx\n/
207
+ @stdio[:stdout].split("\n")[1].should match /^foobar\t/
208
+ @stdio[:stdout].lines.count.should == 2
209
+ end
210
+ end
211
+
212
+ describe "stderr" do
213
+ it "is empty" do
214
+ @stdio[:stderr].should == ''
215
+ end
216
+ end
217
+ end
218
+
219
+ describe 'HtmlWriter' do
220
+ before (:each) do
221
+ StdioTrap.trap!
222
+ output = B::HtmlWriter.new
223
+ B.enchmark('foobar', :output => output) do
224
+ job('a') {}
225
+ end
226
+ @stdio = StdioTrap.release!
227
+ end
228
+
229
+ describe "stdout" do
230
+ it "looks like output from TsvWriter" do
231
+ @stdio[:stdout].should match /^<table>\n<tr><th>/
232
+ @stdio[:stdout].split("\n")[2].should match /^<tr><td>foobar<\/td>/
233
+ @stdio[:stdout].lines.count.should == 4
234
+ end
235
+ end
236
+
237
+ describe "stderr" do
238
+ it "is empty" do
239
+ @stdio[:stderr].should == ''
240
+ end
241
+ end
242
+
243
+ end
244
+ end
@@ -0,0 +1,6 @@
1
+ require 'stdiotrap'
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+
5
+ require 'b'
6
+
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: b
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Moe
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: simplecov
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: guard
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: stdiotrap
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: hitimes
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: blockenspiel
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ description: A small, convenient benchmark-library.
143
+ email:
144
+ - moe@busyloop.net
145
+ executables: []
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - .gitignore
150
+ - Gemfile
151
+ - Guardfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - b.gemspec
156
+ - lib/b.rb
157
+ - lib/b/demo.rb
158
+ - lib/b/output_plugins.rb
159
+ - lib/b/version.rb
160
+ - spec/b_spec.rb
161
+ - spec/spec_helper.rb
162
+ homepage: https://github.com/busyloop/b
163
+ licenses: []
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ none: false
176
+ requirements:
177
+ - - ! '>='
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 1.8.23
183
+ signing_key:
184
+ specification_version: 3
185
+ summary: A small, convenient benchmark-library.
186
+ test_files:
187
+ - spec/b_spec.rb
188
+ - spec/spec_helper.rb