cuke-step-bm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ source "http://rubygems.org"
data/README.rdoc ADDED
@@ -0,0 +1,32 @@
1
+ == Cucumber steps time-consuming
2
+
3
+ figure out which steps are slow.
4
+
5
+ == Configuration
6
+
7
+ * config/cuke_step_bm.rb or ~/.cuke_step_bm
8
+
9
+ output_mode:
10
+ # :bm => BenchMark(only for display)
11
+ # :std => SimpleTimeDiff
12
+ # :std_with_log => SimpleTimeDiff log
13
+ # :off => Cucumber default do nothing
14
+
15
+ CukeStepBm.configure do |config|
16
+ config.root = "/tmp"
17
+ config.output_mode = :std_with_log
18
+ config.log_file = File.join(config.root, 'steps_consuming.bms')
19
+ config.delimiter = "-#{[20879].pack("U*")}-"
20
+ end
21
+
22
+ == Command
23
+
24
+ cuke-step-bm -h for help
25
+
26
+ == TodoList Working in process
27
+
28
+ * add spec test
29
+ * add pp
30
+ * add color
31
+ * refator for something
32
+ * ..
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/cuke-step-bm ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib') unless $:.include?(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require "cuke-step-bm"
5
+
6
+ CukeStepBm::Cli.execute(ARGV.dup)
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "cuke-step-bm"
6
+ s.version = "1.0.0"
7
+ s.authors = ["elvuel"]
8
+ s.email = ["elvuel@gmail.com"]
9
+ s.homepage = "https://github.com/elvuel"
10
+ s.summary = %q{cucumber steps benchmark}
11
+ s.description = %q{cucumber steps benchmark}
12
+
13
+ s.rubyforge_project = "cuke-step-bm"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ # specify any dependencies here; for example:
21
+ # s.add_development_dependency "rspec"
22
+ # s.add_runtime_dependency "rest-client"
23
+ end
@@ -0,0 +1,4 @@
1
+ 1-冏-step1-冏-features/test.feature:1
2
+ 2-冏-step1-冏-features/test.feature:2
3
+ 3-冏-step3-冏-features/test.feature:3
4
+ 20-冏-step4-冏-features/test.feaDture:4
File without changes
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ require "cuke-step-bm/cli"
3
+
4
+ module CukeStepBm
5
+ # cuke_step_bm bm-cuke-step cuke-step-bm.rb
6
+ VERSION = "0.0.1".freeze
7
+
8
+ # :bm => BenchMark(only for display)
9
+ # :std => SimpleTimeDiff
10
+ # :std_with_log => SimpleTimeDiff log
11
+ # :off => Cucumber default do nothing
12
+
13
+ BM_SUPPORTED_OUTPUT_MODES = [:bm, :std, :std_with_log, :off]
14
+
15
+ class << self
16
+ attr_accessor :root, :log_file, :output_mode, :delimiter
17
+
18
+ def configure
19
+ yield self
20
+ end
21
+
22
+ def suite!
23
+ use_defaults!
24
+ paths = [
25
+ File.expand_path("config/cuke_step_bm.rb", root),
26
+ File.expand_path(".cuke_step_bm", root)
27
+ ]
28
+ paths.each { |path| load(path) if File.exist?(path) }
29
+ self.output_mode = :std unless BM_SUPPORTED_OUTPUT_MODES.include? output_mode
30
+ end
31
+
32
+ def use_defaults!
33
+ configure do |config|
34
+ config.root = File.expand_path('.', Dir.pwd)
35
+ config.output_mode = :std
36
+ config.log_file = File.join(config.root, 'features', 'steps_consuming.bms')
37
+ config.delimiter = "-#{[20879].pack("U*")}-"
38
+ end
39
+ end
40
+
41
+ def write_to_log(msg)
42
+ File.open(log_file, "a+") { |f| f.write msg }
43
+ rescue
44
+ nil
45
+ end
46
+
47
+ #remove log file before cuke working with :std_with_log
48
+ def remove_log!
49
+ File.delete log_file if File.exist?(log_file) && (output_mode == :std_with_log)
50
+ rescue Exception => e
51
+ warn e.message
52
+ end
53
+
54
+ end # class << self
55
+ end # CucumberStepsBm
56
+
57
+ CukeStepBm.suite!
58
+
59
+ if defined?(Cucumber) && defined?(Cucumber::VERSION) && (Cucumber::VERSION >= "1.1.1") && (CukeStepBm.output_mode != :off)
60
+ CukeStepBm.remove_log!
61
+
62
+ module Cucumber
63
+ module Ast
64
+ class StepInvocation #:nodoc:
65
+ def invoke(step_mother, configuration)
66
+ exec_proc = Proc.new {
67
+ find_step_match!(step_mother, configuration)
68
+ unless @skip_invoke || configuration.dry_run? || @exception || @step_collection.exception
69
+ @skip_invoke = true
70
+ begin
71
+ @step_match.invoke(@multiline_arg)
72
+ step_mother.after_step
73
+ status!(:passed)
74
+ rescue Pending => e
75
+ failed(configuration, e, false)
76
+ status!(:pending)
77
+ rescue Undefined => e
78
+ failed(configuration, e, false)
79
+ status!(:undefined)
80
+ rescue Cucumber::Ast::Table::Different => e
81
+ @different_table = e.table
82
+ failed(configuration, e, false)
83
+ status!(:failed)
84
+ rescue Exception => e
85
+ failed(configuration, e, false)
86
+ status!(:failed)
87
+ end
88
+ end
89
+ }
90
+
91
+ if (CukeStepBm.output_mode == :bm) && defined?(Benchmark)
92
+ Benchmark.bm do |reporter|
93
+ reporter.report("Time consuming:") { exec_proc.call }
94
+ end
95
+ else
96
+ time_begin = Time.now
97
+ exec_proc.call
98
+ time_end = Time.now
99
+ time_for_output = "Step consume %s seconds.\n" % ["#{time_end - time_begin}"]
100
+ time_consuming_message = ["#{time_end - time_begin}", @name, file_colon_line].join(CukeStepBm.delimiter)
101
+ time_consuming_message << "\n"
102
+ if CukeStepBm.output_mode == :std_with_log
103
+ puts time_for_output
104
+ CukeStepBm.write_to_log time_consuming_message
105
+ else
106
+ puts time_for_output
107
+ end
108
+ end
109
+ end # invoke
110
+
111
+ end # StepInvocation
112
+ end # Ast
113
+ end # Cucumber
114
+ end
@@ -0,0 +1,271 @@
1
+ # encoding: utf-8
2
+
3
+ require 'optparse'
4
+ #require 'ostruct'
5
+
6
+ module CukeStepBm
7
+ class Cli
8
+
9
+ class Options
10
+ #SCOPES = [:step, :feature]
11
+ def parse!(args)
12
+ options = {}
13
+ opt_parser = OptionParser.new("", 30, ' ') do |opts|
14
+ opts.banner = "Usage: cuke-step-bm options"
15
+
16
+ opts.on("-f", "--feature FEATURE", String, "In specify feature") do |feature|
17
+ options[:feature] = feature
18
+ end
19
+
20
+ opts.on("-M", "--most", "Show the most time-consuming step") do
21
+ options[:act] = [ :most ]
22
+ end
23
+
24
+ opts.on("-t", "--total", "Show the total time-consuming") do
25
+ options[:act] = [ :total ]
26
+ end
27
+
28
+ opts.on("-w", "--within from,to", Array, "Show the steps time-consuming within the given range (use: -w 1,2)") do |from_to|
29
+ if from_to.size < 2
30
+ puts "wrong argument size"
31
+ exit
32
+ end
33
+ options[:act] = [ :within, [from_to.first, from_to.last] ]
34
+ end
35
+
36
+ opts.on("-l", "--less VALUE", Float, "Show the steps time-consuming less than or equal the given") do |v|
37
+ options[:act] = [ :less, v ]
38
+ end
39
+
40
+ opts.on("-m", "--more VALUE", Float, "Show the steps time-consuming more than or equal the given") do |v|
41
+ options[:act] = [ :more, v ]
42
+ end
43
+
44
+ #opts.on("-s", "--scope value", "Show messages within scope (all/feature_filepath)") do |v|
45
+ # v = v.to_sym
46
+ # v = :step unless SCOPES.include? v
47
+ # options[:scope] = v
48
+ #end
49
+
50
+ opts.separator ""
51
+ opts.separator "Common options:"
52
+
53
+ opts.on_tail("-h", "--help", "Show this usage message") do
54
+ puts opts
55
+ exit
56
+ end
57
+
58
+ opts.on_tail("-v", "--version", "Show version") do
59
+ puts "cuke-step-bm #{CukeStepBm::VERSION}"
60
+ exit
61
+ end
62
+ end
63
+ opt_parser.parse! args
64
+ options
65
+ end
66
+ end # Options
67
+
68
+
69
+ def self.execute(args)
70
+ new(args).execute
71
+ end
72
+
73
+ #attr_reader :opt_parser
74
+
75
+ def initialize(args)
76
+ @config = CukeStepBm
77
+ @log_file = @config.log_file
78
+ bm_validate!
79
+ @options = Options.new.parse! args
80
+ @options = default_options.merge @options
81
+ @options[:act] = [ :most ] unless @options[:act]
82
+ #@scope = @options.delete :scope
83
+ end
84
+
85
+ def execute
86
+ parse_bms!
87
+ self.send *@options[:act]
88
+ end
89
+
90
+ private
91
+ def most
92
+ the_most = @bms.sort_by { |item| item[:consume] }.last
93
+ if in_feature?
94
+ msg = "The most time-consuming in %s:\n\tLine: %s, Step: [ %s ], take %s seconds." % [
95
+ msg_color(the_most[:feature], "red"),
96
+ the_most[:line],
97
+ msg_color(the_most[:step], "red"),
98
+ msg_color(the_most[:consume])
99
+ ]
100
+ info(msg)
101
+ else
102
+ msg = "The most time-consuming in all features:In %s, Line: %s, Step: [ %s ], take %s seconds.\n\n" % [
103
+ msg_color(the_most[:feature], "red"),
104
+ the_most[:line],
105
+ msg_color(the_most[:step], "red"),
106
+ msg_color(the_most[:consume])
107
+ ]
108
+ info(msg)
109
+ feature_grp_hash = @bms.group_by { |item| item[:feature] }
110
+ feature_grp_hash.each do |feature, value|
111
+ the_most = value.sort_by { |item| item[:consume] }.last
112
+ msg = "In feature [ %s ]\n\tLine: %s, Step: [ %s ], take %s seconds.\n\n" % [
113
+ msg_color(the_most[:feature] || feature, "red"),
114
+ the_most[:line],
115
+ msg_color(the_most[:step], "red"),
116
+ msg_color(the_most[:consume])
117
+ ]
118
+ info(msg)
119
+ end # grp_hash
120
+
121
+ end # in_feature?
122
+ end # most
123
+
124
+ def total
125
+ t_ary = @bms.collect { |item| item[:consume] }
126
+ sum = t_ary.inject(:+)
127
+ if in_feature?
128
+ feature = @bms.first[:feature]
129
+ info "Feature %s, %s steps total consume: %s seconds." % [msg_color(feature), msg_color(t_ary.size), msg_color(sum)]
130
+ else
131
+ msg = "Total consume: %s seconds.\n\n" % msg_color(sum)
132
+ info msg
133
+ feature_grp_hash = @bms.group_by { |item| item[:feature] }
134
+ feature_grp_hash.each do |feature, value|
135
+ t_ary = value.collect { |item| item[:consume] }
136
+ sum = t_ary.inject(:+)
137
+ msg = "\tFeature %s, %s steps total consume: %s seconds.\n\n" % [msg_color(feature), msg_color(t_ary.size), msg_color(sum)]
138
+ info msg
139
+ end
140
+ end # in_feature?
141
+ end # total
142
+
143
+ def within(args)
144
+ from, to = args.collect{ |arg| arg.to_f }
145
+ from, to = to, from if to < from
146
+ #grp_by = @bms.group_by { |item| in_feature? ? item[:step] : item[:feature] }
147
+ if in_feature?
148
+ grp_by_step = @bms.group_by { |item| item[:step] }
149
+ step_mean_hash= grp_by_step.inject({}) do |h, item|
150
+ key = item.shift
151
+ consumings = item.flatten.collect { |bm| bm[:consume] }
152
+ h[key] = consumings.inject(:+) / consumings.size
153
+ h
154
+ end
155
+ results = step_mean_hash.select { |k, v| (v >= from ) && (v <= to) }
156
+ feature = @bms.first[:feature]
157
+ unless results.empty?
158
+ info "In %s, Steps within %s .. %s" % [msg_color(feature), msg_color(from), msg_color(to)]
159
+ end
160
+ results.sort_by { |k, v| v }.each do |k, v|
161
+ info "\t Step %s, it average take %s seconds" % [msg_color(k), msg_color(v, "red")]
162
+ end
163
+ else
164
+ grp_by_feature = @bms.group_by { |item| item[:feature] }
165
+ grp_by_feature.each do |feature, all_steps|
166
+ grp_by_step = all_steps.group_by { |item| item[:step] }
167
+ step_mean_hash= grp_by_step.inject({}) do |h, item|
168
+ key = item.shift
169
+ consumings = item.flatten.collect { |bm| bm[:consume] }
170
+ h[key] = consumings.inject(:+) / consumings.size
171
+ h
172
+ end
173
+ results = step_mean_hash.select { |k, v| (v >= from ) && (v <= to) }
174
+ unless results.empty?
175
+ info "In %s, Steps within %s .. %s" % [msg_color(feature), msg_color(from), msg_color(to)]
176
+ end
177
+ results.sort_by { |k, v| v }.each do |k, v|
178
+ info "\t Step %s, it average take %s seconds" % [msg_color(k), msg_color(v, "red")]
179
+ end
180
+ end # grp_by_feature
181
+ end # in_feature?
182
+ end # within
183
+
184
+ def less_or_more(value, lom = "less")
185
+ value = value.to_f
186
+ if in_feature?
187
+ grp_by_step = @bms.group_by { |item| item[:step] }
188
+ step_mean_hash= grp_by_step.inject({}) do |h, item|
189
+ key = item.shift
190
+ consumings = item.flatten.collect { |bm| bm[:consume] }
191
+ h[key] = consumings.inject(:+) / consumings.size
192
+ h
193
+ end
194
+ results = step_mean_hash.select { |k, v| v.send((lom=="less") ? :<= : :>=, value) }
195
+ feature = @bms.first[:feature]
196
+ unless results.empty?
197
+ info "In %s, Steps %s than %s" % [msg_color(feature), msg_color(lom), msg_color(value)]
198
+ end
199
+ results.sort_by { |k, v| v }.each do |k, v|
200
+ info "\t Step %s, it average take %s seconds" % [msg_color(k), msg_color(v, "red")]
201
+ end
202
+ else
203
+ grp_by_feature = @bms.group_by { |item| item[:feature] }
204
+ grp_by_feature.each do |feature, all_steps|
205
+ grp_by_step = all_steps.group_by { |item| item[:step] }
206
+ step_mean_hash= grp_by_step.inject({}) do |h, item|
207
+ key = item.shift
208
+ consumings = item.flatten.collect { |bm| bm[:consume] }
209
+ h[key] = consumings.inject(:+) / consumings.size
210
+ h
211
+ end
212
+ results = step_mean_hash.select { |k, v| v.send((lom=="less") ? :<= : :>=, value) }
213
+ unless results.empty?
214
+ info "In %s, Steps %s than %s" % [msg_color(feature), msg_color(lom), msg_color(value)]
215
+ end
216
+ results.sort_by { |k, v| v }.each do |k, v|
217
+ info "\t Step %s, it average take %s seconds" % [msg_color(k), msg_color(v, "red")]
218
+ end
219
+ end
220
+ end # in_feature?
221
+ end # less_or_more
222
+
223
+ def less(value)
224
+ less_or_more(value, "less")
225
+ end
226
+
227
+ def more(value)
228
+ less_or_more(value, "more")
229
+ end
230
+
231
+ def default_options
232
+ { scope: :step, feature: :all }
233
+ end
234
+
235
+ def bm_validate!
236
+ abort "please make sure that record log file exist!" unless File.exist? @log_file
237
+ @bms ||= File.read(@log_file)
238
+ @bms.strip!
239
+ abort "record log file is empty!" if @bms.empty?
240
+ end
241
+
242
+ def parse_bms!
243
+ bms = @bms.split("\n")
244
+ bms.delete ""
245
+ bms.compact!
246
+ @bms = bms.inject([]) do |ary, item|
247
+ tmp_ary = item.split(@config.delimiter)
248
+ consume_time, step_name, file_colon_line = tmp_ary
249
+ feature, line = file_colon_line.split(":")
250
+ tmp_ary = { feature: feature, step: step_name, line: line, consume: consume_time.to_f }
251
+ ary << tmp_ary if @options[:feature].is_a?(Symbol) or (@options[:feature] == feature)
252
+ ary
253
+ end
254
+ abort msg_color("empty", "r") if @bms.empty?
255
+ end
256
+
257
+ def in_feature?
258
+ !@options[:feature].is_a?(Symbol)
259
+ end
260
+
261
+ def msg_color(msg, color="green")
262
+ color = (color == "green") ? 32 : 31
263
+ "\033[4;1;#{color};40m#{msg}\033[0m"
264
+ end
265
+
266
+ def info(msg)
267
+ puts msg
268
+ end
269
+
270
+ end
271
+ end
data/test.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/lib') unless $:.include?(File.dirname(__FILE__) + '/lib')
3
+
4
+ require "cuke-step-bm"
5
+
6
+ CukeStepBm::Cli.execute(ARGV.dup)
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cuke-step-bm
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - elvuel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-18 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: cucumber steps benchmark
15
+ email:
16
+ - elvuel@gmail.com
17
+ executables:
18
+ - cuke-step-bm
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - README.rdoc
25
+ - Rakefile
26
+ - bin/cuke-step-bm
27
+ - cuke-step-bm.gemspec
28
+ - features/steps_consuming.bms
29
+ - features/test.feature
30
+ - lib/cuke-step-bm.rb
31
+ - lib/cuke-step-bm/cli.rb
32
+ - test.rb
33
+ homepage: https://github.com/elvuel
34
+ licenses: []
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project: cuke-step-bm
53
+ rubygems_version: 1.8.10
54
+ signing_key:
55
+ specification_version: 3
56
+ summary: cucumber steps benchmark
57
+ test_files: []
58
+ has_rdoc: