benchmark-spec 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.
- checksums.yaml +7 -0
- data/bin/benchmark_spec +4 -0
- data/lib/benchmark_spec.rb +160 -0
- data/lib/benchmark_spec/dsl.rb +26 -0
- data/lib/benchmark_spec/output_formatter.rb +64 -0
- data/lib/benchmark_spec/report_presenter.rb +79 -0
- data/lib/benchmark_spec/shared_context.rb +21 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: be460326ecac3098408c326aa2b2dc8ec6362ed5
|
4
|
+
data.tar.gz: 4fb8619aa143c22645513048380e2a7c1c5ca177
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 665bc58fba734fae65fb9bad218a37212303827d4d40d80c152c16bb82e5b36576fdc49947e05702c561c05bc8e010afded91035fca5725ea323d71fe89c4515
|
7
|
+
data.tar.gz: 7ed88e28f5df29d509f6c9fbd00687db44593c454ca799efe1101e59e52f6b7b65c6779df48004a8f6ba3d55b80f8c858e1d0b08cdb68b4fcb113f7f7f46c5c1
|
data/bin/benchmark_spec
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'colorize'
|
3
|
+
require 'rack/test'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
|
6
|
+
require_relative 'benchmark_spec/dsl'
|
7
|
+
require_relative 'benchmark_spec/shared_context'
|
8
|
+
require_relative 'benchmark_spec/output_formatter'
|
9
|
+
require_relative 'benchmark_spec/report_presenter'
|
10
|
+
|
11
|
+
class BenchmarkSpec
|
12
|
+
include Rack::Test::Methods
|
13
|
+
|
14
|
+
include DSL
|
15
|
+
include SharedContext
|
16
|
+
|
17
|
+
class_attribute :config_proc
|
18
|
+
|
19
|
+
attr_accessor :description, :execution, :tasks, :nested_specs, :before_hooks,
|
20
|
+
:after_hooks, :config, :reports, :formatter
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(description, opts = {})
|
24
|
+
@description = description
|
25
|
+
@execution = opts[:execution]
|
26
|
+
|
27
|
+
@before_hooks = opts[:before_hooks] || {}
|
28
|
+
@after_hooks = opts[:after_hooks] || {}
|
29
|
+
|
30
|
+
@tasks = []
|
31
|
+
@nested_specs = []
|
32
|
+
@reports = []
|
33
|
+
|
34
|
+
@formatter = opts[:formatter] || OutputFormatter.new
|
35
|
+
|
36
|
+
@config ||= {}
|
37
|
+
instance_exec @config, &config_proc
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
def configure(&block)
|
42
|
+
BenchmarkSpec.config_proc = block
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_files_recursively(path)
|
46
|
+
Dir[path].each do |file|
|
47
|
+
if File.directory? file
|
48
|
+
load_files_recursively file + "/*"
|
49
|
+
else
|
50
|
+
require file if File.basename(file).match /benchmark\.rb$/
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def describe(description, &block)
|
56
|
+
@benchmarks ||= []
|
57
|
+
@benchmarks << BenchmarkSpec.new(description, execution: block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def evaluate_all
|
61
|
+
return unless @benchmark
|
62
|
+
|
63
|
+
@benchmarks.each {|b| b.evaluate}
|
64
|
+
end
|
65
|
+
|
66
|
+
def run(args = [])
|
67
|
+
base_path = Dir.pwd
|
68
|
+
|
69
|
+
files_to_run = args.map{|path| "#{base_path}/#{path}"}
|
70
|
+
files_to_run = ["#{base_path}/benchmark"] if files_to_run.empty?
|
71
|
+
|
72
|
+
files_to_run.each do |f|
|
73
|
+
load_files_recursively(f)
|
74
|
+
end
|
75
|
+
|
76
|
+
OutputFormatter.title("Benchmark Spec")
|
77
|
+
|
78
|
+
evaluate_all
|
79
|
+
|
80
|
+
OutputFormatter.title("Results")
|
81
|
+
ensure
|
82
|
+
ReportPresenter.print_results(@benchmarks)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def before(hook = :each, &block)
|
87
|
+
before_hooks[hook] ||= []
|
88
|
+
before_hooks[hook] << block
|
89
|
+
end
|
90
|
+
|
91
|
+
def after(hook = :each, &block)
|
92
|
+
after_hooks[hook] ||= []
|
93
|
+
after_hooks[hook] << block
|
94
|
+
end
|
95
|
+
|
96
|
+
def describe(description, &block)
|
97
|
+
spec_options = {}
|
98
|
+
spec_options[:execution] = block if block
|
99
|
+
spec_options[:before_hooks] = before_hooks.dup
|
100
|
+
spec_options[:after_hooks] = after_hooks.dup
|
101
|
+
spec_options[:formatter] = OutputFormatter.new(indent_level: formatter.indent_level + 1)
|
102
|
+
|
103
|
+
nested_specs << BenchmarkSpec.new(description, spec_options)
|
104
|
+
end
|
105
|
+
|
106
|
+
def benchmark(description, &block)
|
107
|
+
tasks << { description: description, execution: block }
|
108
|
+
end
|
109
|
+
|
110
|
+
def run_all_tasks
|
111
|
+
return if tasks.empty?
|
112
|
+
|
113
|
+
tasks.each do |t|
|
114
|
+
begin
|
115
|
+
pending t[:description] if t[:execution].nil?
|
116
|
+
|
117
|
+
formatter.print "running #{t[:description]} ..."
|
118
|
+
|
119
|
+
before_each_hooks.each {|b| b.call}
|
120
|
+
reports << Benchmark.measure(t[:description]) { t[:execution].call }
|
121
|
+
after_each_hooks.each {|a| a.call}
|
122
|
+
rescue => e
|
123
|
+
formatter.print "Error: #{e.message}".colorize(:red)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def evaluate
|
129
|
+
if execution
|
130
|
+
formatter.print "#{description}".colorize(:light_blue)
|
131
|
+
|
132
|
+
instance_eval &execution
|
133
|
+
|
134
|
+
run_all_tasks
|
135
|
+
|
136
|
+
nested_specs.each do |ns|
|
137
|
+
ns.evaluate
|
138
|
+
end
|
139
|
+
else
|
140
|
+
pending(description)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def pending(description)
|
147
|
+
formatter.print "Pending: #{description}".colorize(:yellow)
|
148
|
+
end
|
149
|
+
|
150
|
+
def before_each_hooks
|
151
|
+
before_hooks[:each] || []
|
152
|
+
end
|
153
|
+
|
154
|
+
def after_each_hooks
|
155
|
+
after_hooks[:each] || []
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
BenchmarkSpec.expose_methods_to_context([:describe, :shared_context], self)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class BenchmarkSpec
|
2
|
+
module DSL
|
3
|
+
def self.included(klass)
|
4
|
+
klass.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def change_global_dsl(context, &changes)
|
9
|
+
(class << context; self; end).class_exec(&changes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def expose_method_dsl_to_context(method, context)
|
13
|
+
change_global_dsl(context) do
|
14
|
+
remove_method(method) if method_defined?(method)
|
15
|
+
define_method(method) do |*a, &b|
|
16
|
+
BenchmarkSpec.__send__(method, *a, &b)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def expose_methods_to_context(methods, context)
|
22
|
+
methods.each {|meth| expose_method_dsl_to_context(meth, context)}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class BenchmarkSpec
|
2
|
+
class OutputFormatter
|
3
|
+
attr_accessor :indent_level, :space_per_indent
|
4
|
+
|
5
|
+
TITLE_LENGHT = 100
|
6
|
+
TITLE_FILLER = '-'
|
7
|
+
MIN_NUM_OF_FILLERS = 20
|
8
|
+
|
9
|
+
def initialize(indent_level: 0, space_per_indent: 2)
|
10
|
+
@indent_level = indent_level
|
11
|
+
@space_per_indent = space_per_indent
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def title(content)
|
16
|
+
with_margins_vertically do
|
17
|
+
side_fillers =
|
18
|
+
TITLE_FILLER * ([TITLE_LENGHT - content.length, MIN_NUM_OF_FILLERS].max / 2 - 1) # -1 for the space
|
19
|
+
|
20
|
+
puts "#{side_fillers} #{content} #{side_fillers}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_margins_vertically(num_of_lines = 1)
|
25
|
+
num_of_lines.times { new_line }
|
26
|
+
yield
|
27
|
+
num_of_lines.times { new_line }
|
28
|
+
end
|
29
|
+
|
30
|
+
def new_line
|
31
|
+
puts "\n"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def title(content)
|
36
|
+
self.class.title(content)
|
37
|
+
end
|
38
|
+
|
39
|
+
def new_line
|
40
|
+
self.class.new_line
|
41
|
+
end
|
42
|
+
|
43
|
+
def with_margins_vertically(*a, &b)
|
44
|
+
self.class.with_margins_vertically(*a, &b)
|
45
|
+
end
|
46
|
+
|
47
|
+
def print(message)
|
48
|
+
message = indent_message(message)
|
49
|
+
puts message
|
50
|
+
end
|
51
|
+
|
52
|
+
def indent_message(message)
|
53
|
+
return message unless indent_level > 0
|
54
|
+
|
55
|
+
message = message.gsub("\n", "\n" + margin)
|
56
|
+
|
57
|
+
return "#{margin}#{message}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def margin
|
61
|
+
" " * indent_level * space_per_indent
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class BenchmarkSpec
|
2
|
+
class ReportPresenter
|
3
|
+
attr_accessor :benchmark, :formatter, :nested_reports, :round_to
|
4
|
+
|
5
|
+
def initialize(benchmark, opts = {}, formatter: nil)
|
6
|
+
@benchmark = benchmark
|
7
|
+
@formatter = formatter || BenchmarkSpec::OutputFormatter.new
|
8
|
+
|
9
|
+
@round_to = opts[:round_to] || 3
|
10
|
+
|
11
|
+
@nested_reports = []
|
12
|
+
@benchmark.nested_specs.map do |ns|
|
13
|
+
child_formatter = BenchmarkSpec::OutputFormatter.new(indent_level: @formatter.indent_level + 1)
|
14
|
+
@nested_reports << ReportPresenter.new(ns, formatter: child_formatter)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def print
|
19
|
+
formatter.print "#{benchmark.description}:".colorize(:light_blue)
|
20
|
+
benchmark.reports.each do |r|
|
21
|
+
formatter.with_margins_vertically do
|
22
|
+
formatter.print "#{r.label}:".colorize(:cyan).underline
|
23
|
+
formatter.new_line
|
24
|
+
formatter.print " User: #{r.utime.round(round_to)}s"
|
25
|
+
formatter.print " System: #{r.stime.round(round_to)}s"
|
26
|
+
formatter.print " Total: #{r.total.round(round_to)}s"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if nested_reports
|
31
|
+
nested_reports.each do |nr|
|
32
|
+
nr.print
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.print_results(benchmarks)
|
38
|
+
benchmarks ||= []
|
39
|
+
|
40
|
+
reports = benchmarks.map{|b| ReportPresenter.new(b)}
|
41
|
+
reports.each{|r| r.print}
|
42
|
+
print_summary(benchmarks)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.print_summary(benchmarks)
|
46
|
+
benchmarks ||= []
|
47
|
+
|
48
|
+
num_of_examples_run = benchmarks.map{|b| calculate_num_of_examples_run(b)}.reduce(:+) || 0
|
49
|
+
total_time = benchmarks.map{|b| calculate_total_time(b)}.reduce(:+) || 0
|
50
|
+
|
51
|
+
puts "--------------------------------------------------------------------"
|
52
|
+
puts "Examples run: #{num_of_examples_run}, Total Execution Time: #{total_time}s".colorize(:light_green)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.calculate_num_of_examples_run(benchmark)
|
56
|
+
return benchmark.reports.count if benchmark.nested_specs.empty?
|
57
|
+
|
58
|
+
num_of_examples_run = benchmark.reports.count
|
59
|
+
benchmark.nested_specs.each do |ns|
|
60
|
+
num_of_examples_run += calculate_num_of_examples_run(ns)
|
61
|
+
end
|
62
|
+
|
63
|
+
num_of_examples_run
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.calculate_total_time(benchmark)
|
67
|
+
if benchmark.nested_specs.empty?
|
68
|
+
return benchmark.reports.map(&:total).reduce(:+) || 0
|
69
|
+
end
|
70
|
+
|
71
|
+
total_time = benchmark.reports.map(&:total).reduce(:+) || 0
|
72
|
+
benchmark.nested_specs.each do |ns|
|
73
|
+
total_time += calculate_total_time(ns)
|
74
|
+
end
|
75
|
+
|
76
|
+
total_time
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class BenchmarkSpec
|
2
|
+
module SharedContext
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
@@shared_context ||= {}
|
6
|
+
|
7
|
+
class << klass
|
8
|
+
def shared_context(context_name, &block)
|
9
|
+
@@shared_context[context_name] = block
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def include_context(context_name)
|
17
|
+
raise "Shared context #{context_name.to_s} does not exist!" if @@shared_context[context_name].nil?
|
18
|
+
instance_eval &@@shared_context[context_name]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: benchmark-spec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Edward Tam
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: etam@adparlor.com
|
15
|
+
executables:
|
16
|
+
- benchmark_spec
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/benchmark_spec
|
21
|
+
- lib/benchmark_spec.rb
|
22
|
+
- lib/benchmark_spec/dsl.rb
|
23
|
+
- lib/benchmark_spec/output_formatter.rb
|
24
|
+
- lib/benchmark_spec/report_presenter.rb
|
25
|
+
- lib/benchmark_spec/shared_context.rb
|
26
|
+
homepage:
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.2.2
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: RSpec syntax mimicked benchmarking framework
|
50
|
+
test_files: []
|
51
|
+
has_rdoc:
|