benchmark-spec 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: