cucumber-profiler 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,29 @@
1
+ h1. cucumber-profiler
2
+
3
+ _Currently_ _tested_ _against_ _cucumber-1.2.1_. _Is_ _known_ _not_ _to_ _be_ _compatible_ _with_ _some_ _older_ _versions_.
4
+
5
+ A way to profile the performance of your cucumber features.
6
+
7
+ The profiler groups your cucumber tests by feature. Within a feature, the profiler times each scenario and calculates the mean and standard deviation. Each scenario that is two or more standard deviations above the mean is listed.
8
+
9
+ h2. Installation
10
+
11
+ 1. Put the profiler in your @Gemfile@
12
+
13
+ bc. gem cucumber-profiler
14
+
15
+ 2. Use the @format@ flag to use the formatter
16
+
17
+ bc. cucumber --format Cucumber::Formatter::Profiler features
18
+
19
+ Alternatively, you can make the profiler your default format by putting the following your @cucumber.yml@ file:
20
+
21
+ bc. --format Cucumber::Formatter::Profiler
22
+
23
+ _If you use Timecop_: If you use the timecop gem to freeze or change time be sure to also return the
24
+ time after each of your tests. Not doing so will make it appear as if your tests are taking either
25
+ far too long or have taken negative time to complete.
26
+
27
+ h2. Copyright
28
+
29
+ Copyright (c) 2012 Michael Blumberg.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "cucumber-profiler"
3
+ s.version = "1.0.0"
4
+ s.platform = Gem::Platform::RUBY
5
+ s.authors = ["Michael Blumberg"]
6
+ s.email = ["mblum14@gmail.com"]
7
+ s.homepage = "https://github.com/mblum14/cucumber-profiler"
8
+ s.summary = %q{A way to profile the performance of your cucumber features}
9
+ s.description = %q{A way to profile the performance of your cucumber features}
10
+
11
+ s.rubyforge_project = "cucumber-profiler"
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_dependency('cucumber', ["~> 1.2.1"])
19
+ end
@@ -0,0 +1 @@
1
+ require 'cucumber/formatter/profiler'
@@ -0,0 +1,218 @@
1
+ require 'cucumber/formatter/io'
2
+ require 'cucumber/formatter/console'
3
+
4
+ module Math::Array
5
+ def sum
6
+ self.inject(0){ |accum, i| accum + i }
7
+ end
8
+
9
+ def mean
10
+ self.sum/self.length.to_f
11
+ end
12
+
13
+ def sample_variance
14
+ m = self.mean
15
+ sum = self.inject(0) { |accum, i| accum + (i - m) ** 2 }
16
+ sum / (self.length).to_f
17
+ end
18
+
19
+ def standard_deviation
20
+ return Math.sqrt(self.sample_variance)
21
+ end
22
+ end
23
+
24
+ module Cucumber
25
+ module Ast
26
+ module Benchmark
27
+ attr_accessor :started_at, :finished_at, :run_time
28
+ end
29
+ end
30
+ module Formatter
31
+ class Profiler
32
+ include Console
33
+ include Io
34
+
35
+ attr_reader :step_mother
36
+
37
+ def initialize(step_mother, path_or_io, options)
38
+ @step_mother, @io, @options, @durations = step_mother, ensure_io(path_or_io, "fuubar"), options, []
39
+ @timed_features = {}
40
+ end
41
+
42
+
43
+ def after_features(features)
44
+ @io.puts
45
+ @io.puts
46
+ print_summary(features)
47
+ end
48
+
49
+
50
+ def before_feature_element(feature_element)
51
+ feature_element.extend Cucumber::Ast::Benchmark
52
+ feature_element.started_at = Time.now
53
+ @exception_raised = false
54
+ end
55
+
56
+ def after_feature_element(feature_element)
57
+ feature_element.finished_at = Time.now
58
+ feature_element.run_time = feature_element.finished_at - feature_element.started_at
59
+ key = feature_element.feature.title
60
+ if @timed_features.has_key? key
61
+ @timed_features[key] = @timed_features[key] << feature_element
62
+ else
63
+ @timed_features[key] = [feature_element]
64
+ end
65
+
66
+ progress(:failed) if @exception_raised
67
+ @exception_raised = false
68
+ end
69
+
70
+ def before_steps(*args)
71
+ progress(:failed) if @exception_raised
72
+ @exception_raised = false
73
+ end
74
+
75
+ def after_steps(*args)
76
+ @exception_raised = false
77
+ end
78
+
79
+ def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
80
+ progress(status)
81
+ @status = status
82
+ end
83
+
84
+ def before_outline_table(outline_table)
85
+ @outline_table = outline_table
86
+ end
87
+
88
+ def after_outline_table(outline_table)
89
+ @outline_table = nil
90
+ end
91
+
92
+ def table_cell_value(value, status)
93
+ return unless @outline_table
94
+ status ||= @status
95
+ progress(status) unless table_header_cell?(status)
96
+ end
97
+
98
+ def exception(*args)
99
+ @exception_raised = true
100
+ end
101
+
102
+ private
103
+
104
+ SUB_SECOND_PRECISION = 5
105
+ DEFAULT_PRECISION = 2
106
+
107
+ def print_summary(features)
108
+ @timed_features.each_key do |feature_name|
109
+ print_report(@timed_features[feature_name], feature_name)
110
+ end
111
+
112
+ @io.puts
113
+ @io.puts
114
+ print_steps(:pending)
115
+ print_steps(:failed)
116
+ print_stats(features, @options)
117
+ end
118
+
119
+ def print_report(feature_elements, feature_name)
120
+ features_elements = feature_elements.sort_by do |e|
121
+ e.run_time
122
+ end.reverse
123
+
124
+ times = feature_elements.map { |e| e.run_time }
125
+ times.extend Math::Array
126
+ mean = times.mean
127
+ stddev = times.standard_deviation
128
+ k = 2
129
+ feature_elements.reject! { |e| (e.run_time < (mean + k * stddev)) || (stddev == 0) }
130
+
131
+ @io.puts "\n\nFeature: #{bold magenta(feature_name)}"
132
+ @io.puts "#{bold red(feature_elements.size)} of #{bold green(times.size)} scenarios(s) were 2 or greater standard deviations above the mean"
133
+ @io.puts cyan "#{"Mean execution time:"} #{format_time(mean)}"
134
+ @io.puts cyan "#{"Standard Deviation:"} #{"%.5f" % stddev}"
135
+ @io.puts red "WARNING: Slow mean execution time!" if mean > 3
136
+
137
+ feature_elements.each_with_index do |example, i|
138
+ @io.puts cyan("#{i+1}.\t#{format_time(example.run_time)}") + white(" \t#{example.title}")
139
+ end
140
+ end
141
+
142
+ CHARS = {
143
+ :passed => '.',
144
+ :failed => 'F',
145
+ :undefined => 'U',
146
+ :pending => 'P',
147
+ :skipped => '-'
148
+ }
149
+
150
+ def progress(status)
151
+ char = CHARS[status]
152
+ @io.print(format_string(char, status))
153
+ @io.flush
154
+ end
155
+
156
+ def table_header_cell?(status)
157
+ status == :skipped_param
158
+ end
159
+
160
+ def format_time(duration)
161
+ if duration > 60
162
+ minutes = duration.to_i / 60
163
+ seconds = duration - minutes * 60
164
+
165
+ red "#{minutes}m #{format_seconds(seconds)}s"
166
+ elsif duration > 10
167
+ red "#{format_seconds(duration)}s"
168
+ elsif duration > 3
169
+ yellow "#{format_seconds(duration)}s"
170
+ else
171
+ "#{format_seconds(duration)}s"
172
+ end
173
+ end
174
+
175
+ def format_seconds(float)
176
+ precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION
177
+ sprintf("%.#{precision}f", float)
178
+ end
179
+
180
+ def color(text, color_code)
181
+ "#{color_code}#{text}\e[0m"
182
+ end
183
+
184
+ def bold(text)
185
+ color(text, "\e[1m")
186
+ end
187
+
188
+ def red(text)
189
+ color(text, "\e[31m")
190
+ end
191
+
192
+ def green(text)
193
+ color(text, "\e[32m")
194
+ end
195
+
196
+ def yellow(text)
197
+ color(text, "\e[33m")
198
+ end
199
+
200
+ def blue(text)
201
+ color(text, "\e[34m")
202
+ end
203
+
204
+ def magenta(text)
205
+ color(text, "\e[35m")
206
+ end
207
+
208
+ def cyan(text)
209
+ color(text, "\e[36m")
210
+ end
211
+
212
+ def white(text)
213
+ color(text, "\e[37m")
214
+ end
215
+
216
+ end
217
+ end
218
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cucumber-profiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Blumberg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cucumber
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.1
30
+ description: A way to profile the performance of your cucumber features
31
+ email:
32
+ - mblum14@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - README.textile
40
+ - Rakefile
41
+ - cucumber-profiler.gemspec
42
+ - lib/cucumber-profiler.rb
43
+ - lib/cucumber/formatter/profiler.rb
44
+ homepage: https://github.com/mblum14/cucumber-profiler
45
+ licenses: []
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project: cucumber-profiler
64
+ rubygems_version: 1.8.24
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: A way to profile the performance of your cucumber features
68
+ test_files: []