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.
@@ -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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'benchmark_spec'
4
+ BenchmarkSpec.run
@@ -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: