cucumber_characteristics 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +14 -0
  5. data/README.md +39 -3
  6. data/Rakefile +45 -1
  7. data/cucumber_characteristics.gemspec +17 -16
  8. data/cucumber_version/1.3.5/Gemfile +4 -0
  9. data/cucumber_version/1.3.5/output_path.rb +1 -0
  10. data/cucumber_version/2.0.2/Gemfile +4 -0
  11. data/cucumber_version/2.0.2/output_path.rb +1 -0
  12. data/cucumber_version/2.1.0/Gemfile +4 -0
  13. data/cucumber_version/2.1.0/output_path.rb +1 -0
  14. data/cucumber_version/2.2.0/Gemfile +4 -0
  15. data/cucumber_version/2.2.0/output_path.rb +1 -0
  16. data/cucumber_version/2.3.3/Gemfile +4 -0
  17. data/cucumber_version/2.3.3/output_path.rb +1 -0
  18. data/features/characteristics/cucumber_step_characteristics.html +204 -54
  19. data/features/characteristics/cucumber_step_characteristics.json +1 -1
  20. data/features/step_definitions/fail_steps.rb +1 -1
  21. data/features/step_definitions/unused_steps.rb +2 -0
  22. data/features/support/env.rb +2 -0
  23. data/lib/cucumber_characteristics.rb +10 -5
  24. data/lib/cucumber_characteristics/autoload.rb +3 -1
  25. data/lib/cucumber_characteristics/configuration.rb +3 -6
  26. data/lib/cucumber_characteristics/cucumber_1x_step_patch.rb +38 -0
  27. data/lib/cucumber_characteristics/cucumber_2x_step_patch.rb +163 -0
  28. data/lib/cucumber_characteristics/{cucumber_step_patch.rb → cucumber_common_step_patch.rb} +4 -5
  29. data/lib/cucumber_characteristics/exporter.rb +6 -10
  30. data/lib/cucumber_characteristics/formatter.rb +0 -23
  31. data/lib/cucumber_characteristics/profile_data.rb +45 -63
  32. data/lib/cucumber_characteristics/version.rb +1 -1
  33. data/lib/cucumber_characteristics/view/step_report.html.haml +35 -33
  34. data/spec/html_output_spec.rb +179 -0
  35. data/spec/spec_helper.rb +40 -0
  36. metadata +71 -51
@@ -1 +1 @@
1
- [["features/step_definitions/wait_steps.rb:1",{"total_count":8,"passed":{"count":8,"feature_location":{"features/scenario_outline.feature:4":[0.102381726,0.300628711],"features/scenario_outline.feature:5":[0.202134351,0.202134351],"features/scenario_outline.feature:6":[0.300628711,0.102381726],"features/scenario_outline.feature:7":[0.202134351,0.202134351]}},"failed":{"count":0,"feature_location":{}},"skipped":{"count":0,"feature_location":{}},"undefined":{"count":0,"feature_location":{}},"regexp":"/^I wait ([\\d\\.]+) seconds$/","fastest":0.102381726,"slowest":0.300628711,"average":0.20181978475,"total_duration":1.614558278,"standard_deviation":0.07009159960098225,"variation":0.198246985,"variance":0.0049128323346244165}]]
1
+ [["features/step_definitions/wait_steps.rb:1",{"total_count":33,"passed":{"count":31,"feature_location":{"features/failure.feature:4":[1.002],"features/pending.feature:4":[1.002],"features/scenario.feature:4":[0.501],"features/scenario.feature:5":[0.501],"features/scenario.feature:6":[0.501],"features/scenario_outline.feature:4":[0.101,0.305],"features/scenario_outline.feature:5":[0.2,0.2],"features/scenario_outline.feature:6":[0.305,0.101],"features/scenario_outline.feature:7":[0.2,0.2],"features/scenario_outline_with_background.feature:4":[0.402,0.402],"features/scenario_outline_with_background.feature:7":[0.101,0.501],"features/scenario_outline_with_background.feature:8":[0.2,0.601],"features/scenario_outline_with_background.feature:9":[0.305,0.701],"features/scenario_outline_with_background.feature:10":[0.2,0.2],"features/scenario_with_background.feature:4":[0.402,0.402],"features/scenario_with_background.feature:7":[0.101],"features/scenario_with_background.feature:8":[0.2],"features/scenario_with_background.feature:9":[0.305],"features/scenario_with_background.feature:12":[0.501],"features/scenario_with_background.feature:13":[0.601],"features/scenario_with_background.feature:14":[0.701]}},"failed":{"count":0,"feature_location":{}},"skipped":{"count":2,"feature_location":{"features/failure.feature:6":[],"features/pending.feature:6":[]}},"undefined":{"count":0,"feature_location":{}},"regexp":"/^I wait ([\\d\\.]+) seconds$/","fastest":0.101,"slowest":1.002,"average":0.3853225806451612,"total_duration":11.944999999999997,"standard_deviation":0.2372462157958885,"variation":0.901,"variance":0.0562857669094693}],["I call a pending step",{"total_count":1,"passed":{"count":0,"feature_location":{}},"failed":{"count":0,"feature_location":{}},"skipped":{"count":0,"feature_location":{}},"undefined":{"count":1,"feature_location":{"features/pending.feature:5":[]}},"fastest":null,"slowest":null,"average":null,"total_duration":null,"standard_deviation":null,"variation":null}],["features/step_definitions/fail_steps.rb:1",{"total_count":1,"passed":{"count":0,"feature_location":{}},"failed":{"count":1,"feature_location":{"features/failure.feature:5":[]}},"skipped":{"count":0,"feature_location":{}},"undefined":{"count":0,"feature_location":{}},"regexp":"/^I fail$/","fastest":null,"slowest":null,"average":null,"total_duration":null,"standard_deviation":null,"variation":null}]]
@@ -1,3 +1,3 @@
1
1
  Given(/^I fail$/) do
2
- fail "Expected step failure"
2
+ raise 'Expected step failure'
3
3
  end
@@ -0,0 +1,2 @@
1
+ Given(/^I am unused/) do
2
+ end
@@ -0,0 +1,2 @@
1
+ require 'cucumber'
2
+ require 'cucumber_characteristics/autoload'
@@ -1,8 +1,13 @@
1
- require "cucumber_characteristics/configuration"
2
- require "cucumber_characteristics/cucumber_step_patch"
3
- require "cucumber_characteristics/exporter"
4
- require "cucumber_characteristics/formatter"
5
- require "cucumber_characteristics/profile_data"
1
+ require 'cucumber_characteristics/configuration'
2
+ require 'cucumber_characteristics/cucumber_common_step_patch'
3
+ if Cucumber::VERSION < '2.0.0'
4
+ require 'cucumber_characteristics/cucumber_1x_step_patch'
5
+ else
6
+ require 'cucumber_characteristics/cucumber_2x_step_patch'
7
+ end
8
+ require 'cucumber_characteristics/exporter'
9
+ require 'cucumber_characteristics/formatter'
10
+ require 'cucumber_characteristics/profile_data'
6
11
 
7
12
  module CucumberCharacteristics
8
13
  class << self
@@ -1,3 +1,5 @@
1
+ require 'cucumber_characteristics'
2
+
1
3
  AfterConfiguration do |configuration|
2
- configuration.options[:formats] << ['CucumberCharacteristics::Formatter', nil]
4
+ configuration.formats << ['CucumberCharacteristics::Formatter', nil]
3
5
  end
@@ -1,15 +1,13 @@
1
1
  module CucumberCharacteristics
2
-
3
2
  class Configuration
4
-
5
3
  attr_accessor :export_json, :export_html, :target_filename, :relative_path, :precision
6
4
 
7
5
  def initialize
8
6
  @export_json = true
9
7
  @export_html = true
10
8
  @precision = 4
11
- @target_filename = 'cucumber_step_characteristics'
12
- @relative_path = 'features/characteristics'
9
+ @target_filename = 'cucumber_step_characteristics'
10
+ @relative_path = 'features/characteristics'
13
11
  end
14
12
 
15
13
  def full_target_filename
@@ -18,7 +16,7 @@ module CucumberCharacteristics
18
16
 
19
17
  def full_dir
20
18
  dir = resolve_path_from_root @relative_path
21
- FileUtils.mkdir_p dir unless File.exists? dir
19
+ FileUtils.mkdir_p dir unless File.exist? dir
22
20
  dir
23
21
  end
24
22
 
@@ -31,6 +29,5 @@ module CucumberCharacteristics
31
29
  File.expand_path(rel_path, Dir.pwd)
32
30
  end
33
31
  end
34
-
35
32
  end
36
33
  end
@@ -0,0 +1,38 @@
1
+ module Cucumber
2
+ class Runtime
3
+ def scenario_profiles
4
+ return @scenario_profiles if @scenario_profiles
5
+ feature_profiles = {}
6
+ scenarios.each do |f|
7
+ if f.is_a?(Cucumber::Ast::OutlineTable::ExampleRow)
8
+ feature_id = f.scenario_outline.file_colon_line
9
+ feature_profiles[feature_id] ||= { name: f.scenario_outline.name, total_duration: 0, step_count: 0, example_count: 0, examples: {} }
10
+ example_id = f.name
11
+ feature_profiles[feature_id][:examples][example_id] = scenario_outline_example_profile(f)
12
+ else
13
+ feature_id = f.file_colon_line
14
+ feature_profiles[feature_id] = scenario_profile(f)
15
+ end
16
+ end
17
+ @scenario_profiles = feature_profiles
18
+ end
19
+
20
+ private
21
+
22
+ def scenario_profile(scenario)
23
+ scenario_profile = { name: scenario.name, total_duration: 0, step_count: 0 }
24
+ scenario_profile[:total_duration] = scenario.steps.select { |s| s.status == :passed }.map { |s| s.step_match.duration }.inject(&:+)
25
+ scenario_profile[:step_count] = scenario.steps.count
26
+ scenario_profile[:status] = scenario.status
27
+ scenario_profile
28
+ end
29
+
30
+ def scenario_outline_example_profile(scenario)
31
+ example_profile = { total_duration: 0, step_count: 0 }
32
+ example_profile[:total_duration] = scenario.instance_variable_get(:@step_invocations).select { |s| s.status == :passed }.map { |s| s.step_match.duration }.inject(&:+)
33
+ example_profile[:step_count] = scenario.instance_variable_get(:@step_invocations).count
34
+ example_profile[:status] = scenario.status
35
+ example_profile
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,163 @@
1
+ module Cucumber
2
+ class StepDefinitionLight
3
+ unless method_defined?(:file_colon_line)
4
+ def file_colon_line
5
+ location.file_colon_line
6
+ end
7
+ end
8
+ end
9
+
10
+ module Core
11
+ module Ast
12
+ module Location
13
+ # Cucumber::Core::Ast::Location::Precise
14
+ class Precise
15
+ unless method_defined?(:file_colon_line)
16
+ def file_colon_line
17
+ to_s
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ module Formatter
26
+ module LegacyApi
27
+ module Ast
28
+ class Scenario
29
+ attr_accessor :steps, :background_steps
30
+ end
31
+ end
32
+
33
+ class RuntimeFacade
34
+ def scenario_profiles
35
+ return @scenario_profiles if @scenario_profiles
36
+ feature_profiles = {}
37
+
38
+ assign_steps_to_scenarios!
39
+ results.scenarios.each do |scenario|
40
+ # Feature id outline is the top level feature defintion
41
+ # aggregating up multiple examples
42
+ feature_id = feature_id(scenario)
43
+ if outline_feature?(scenario)
44
+ feature_profiles[feature_id] ||= { name: scenario.name, total_duration: 0, step_count: 0, examples: {} }
45
+ agg_steps = aggregate_steps(scenario.steps)
46
+ feature_profiles[feature_id][:total_duration] += agg_steps[:total_duration]
47
+ feature_profiles[feature_id][:step_count] += agg_steps[:step_count]
48
+
49
+ # First order step associations to scenario examples
50
+ example_id = scenario_from(scenario.steps.first).name.match(/(Examples.*\))/).captures.first
51
+ feature_profiles[feature_id][:examples][example_id] = { total_duration: 0, step_count: 0 }
52
+ feature_profiles[feature_id][:examples][example_id] = aggregate_steps(scenario.steps)
53
+ feature_profiles[feature_id][:examples][example_id][:status] = scenario.status.to_sym
54
+ else
55
+ feature_profiles[feature_id] = { name: scenario.name, total_duration: 0, step_count: 0 }
56
+ feature_profiles[feature_id][:status] = scenario.status.to_sym
57
+ feature_profiles[feature_id].merge!(aggregate_steps(scenario.steps))
58
+ end
59
+ end
60
+ # Collect up background tasks not directly attributable to
61
+ # specific scenarios
62
+ feature_files.each do |file|
63
+ steps = background_steps_for(file)
64
+ next if steps.empty?
65
+ feature_id = "#{file}:0 (Background)"
66
+ feature_profiles[feature_id] = { name: 'Background', total_duration: 0, step_count: 0 }
67
+ feature_profiles[feature_id].merge!(aggregate_steps(steps))
68
+ feature_profiles[feature_id][:status] =
69
+ steps.map(&:status).uniq.join(',')
70
+ end
71
+
72
+ @scenario_profiles = feature_profiles
73
+ end
74
+
75
+ private
76
+
77
+ def feature_id(scenario)
78
+ if outline_feature?(scenario)
79
+ scenario_outline_to_feature_id(scenario)
80
+ else
81
+ scenario.location.to_s
82
+ end
83
+ end
84
+
85
+ def outline_feature?(scenario)
86
+ scenario.name =~ /Examples \(#\d+\)$/
87
+ end
88
+
89
+ def outline_step?(step)
90
+ step.step.class == Cucumber::Core::Ast::ExpandedOutlineStep
91
+ end
92
+
93
+ def background_step?(step)
94
+ !step.background.nil?
95
+ end
96
+
97
+ def scenario_from(step)
98
+ if outline_step?(step)
99
+ # Match directly to scenario by line number
100
+ scenarios.select do |s|
101
+ s.location.file == step.location.file && s.location.line == step.location.line
102
+ end.first
103
+ else
104
+ # Match indirectly to preceeding scenario by line number
105
+ # (explicit sort needed for ruby 2.x)
106
+ scenarios.select { |s| s.location.file == step.location.file && s.location.line < step.location.line }.sort { |a, b| a.location.line <=> b.location.line }.last
107
+ end
108
+ end
109
+
110
+ def background_steps_for(scenario_file)
111
+ steps.select do |s|
112
+ s.location.file == scenario_file &&
113
+ background_step?(s)
114
+ end
115
+ end
116
+
117
+ def assign_steps_to_scenarios!
118
+ steps.each do |step|
119
+ scenario = scenario_from(step)
120
+ if scenario
121
+ scenario.steps ||= []
122
+ scenario.steps << step
123
+ end
124
+ end
125
+ end
126
+
127
+ Feature = Struct.new(:name, :line) #=> Customer
128
+ def scenario_outlines_from_file(file)
129
+ count = 1
130
+ results = []
131
+ File.open(file).each_line do |li|
132
+ results << Feature.new(li, count) if li[/^\s*Scenario Outline:/]
133
+ count += 1
134
+ end
135
+ results
136
+ end
137
+
138
+ # List of scenario name and lines from file
139
+ def scenario_outlines(file)
140
+ @feature_outlines ||= {}
141
+ return @feature_outlines[file] if @feature_outlines[file]
142
+ @feature_outlines[file] = scenario_outlines_from_file(file)
143
+ end
144
+
145
+ def scenario_outline_to_feature_id(scenario)
146
+ scenarios = scenario_outlines(scenario.location.file)
147
+ scenario_outline = scenarios.select { |s| s.line < scenario.location.line }
148
+ scenario_outline = scenario_outline.sort { |a, b| a.line <=> b.line }.last
149
+ "#{scenario.location.file}:#{scenario_outline.line}"
150
+ end
151
+
152
+ def feature_files
153
+ scenarios.map { |s| s.location.file }.uniq
154
+ end
155
+
156
+ def aggregate_steps(steps)
157
+ { total_duration: steps.reject { |s| [:skipped, :undefined].include?(s.status) }.map { |s| s.duration.nanoseconds.to_f / 1_000_000_000 }.inject(&:+),
158
+ step_count: steps.count }
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -1,14 +1,14 @@
1
- #http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i
1
+ # http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i
2
2
 
3
3
  module Cucumber
4
4
  class StepMatch
5
- if self.method_defined?(:invoke)
5
+ if method_defined?(:invoke)
6
6
  old_invoke = instance_method(:invoke)
7
7
  attr_reader :duration
8
8
 
9
- define_method(:invoke) do | multiline_arg |
9
+ define_method(:invoke) do |multiline_arg|
10
10
  start_time = Time.now
11
- ret = old_invoke.bind(self).(multiline_arg)
11
+ ret = old_invoke.bind(self).call(multiline_arg)
12
12
  @duration = Time.now - start_time
13
13
  ret
14
14
  end
@@ -16,7 +16,6 @@ module Cucumber
16
16
  end
17
17
  end
18
18
 
19
-
20
19
  module Cucumber
21
20
  module Ast
22
21
  class StepInvocation
@@ -2,9 +2,7 @@ require 'haml'
2
2
  require 'digest/md5'
3
3
 
4
4
  module CucumberCharacteristics
5
-
6
5
  class Exporter
7
-
8
6
  attr_reader :profile
9
7
  def initialize(profile)
10
8
  @profile = profile
@@ -14,11 +12,11 @@ module CucumberCharacteristics
14
12
  def export
15
13
  filename = @config.full_target_filename
16
14
  if @config.export_html
17
- File.open(filename+'.html', 'w') { |file| file.write(to_html) }
15
+ File.open(filename + '.html', 'w') { |file| file.write(to_html) }
18
16
  puts "Step characteristics report written to #{filename}.html"
19
17
  end
20
18
  if @config.export_json
21
- File.open(filename+'.json', 'w') { |file| file.write(to_json) }
19
+ File.open(filename + '.json', 'w') { |file| file.write(to_json) }
22
20
  puts "Step characteristics report written to #{filename}.json"
23
21
  end
24
22
  end
@@ -36,20 +34,18 @@ module CucumberCharacteristics
36
34
  # HELPERS
37
35
 
38
36
  def format_ts(t)
39
- t ? sprintf("%0.#{@config.precision}f", t) : '-'
37
+ t ? format("%0.#{@config.precision}f", t) : '-'
40
38
  end
41
39
 
42
40
  def format_step_usage(step_feature_data)
43
41
  step_feature_data[:feature_location].map do |location, timings|
44
- "#{location}" + (timings.count > 1 ? " (x#{timings.count})" : '')
42
+ location.to_s + (timings.count > 1 ? " (x#{timings.count})" : '')
45
43
  end.join("\n")
46
44
  end
47
45
 
48
46
  def step_status_summary(profile)
49
47
  status = profile.step_count_by_status
50
- status.keys.sort.map{|s| status[s]> 0 ? "#{s.capitalize}: #{status[s]}" : nil}.compact.join(', ')
48
+ status.keys.sort.map { |s| status[s] > 0 ? "#{s.capitalize}: #{status[s]}" : nil }.compact.join(', ')
51
49
  end
52
-
53
- end
54
-
50
+ end
55
51
  end
@@ -1,7 +1,5 @@
1
1
  module CucumberCharacteristics
2
-
3
2
  class Formatter
4
-
5
3
  def initialize(runtime, io, options)
6
4
  @runtime = runtime
7
5
  @io = io
@@ -9,30 +7,9 @@ module CucumberCharacteristics
9
7
  @features = {}
10
8
  end
11
9
 
12
- # def before_feature(scenario)
13
- # pp scenario.class.name
14
- # if scenario.is_a?(Cucumber::Ast::OutlineTable::ExampleRow)
15
- # feature_location = scenario.scenario_outline.file_colon_line
16
- # feature_name = scenario.scenario_outline.name.chomp
17
- # feature_example = scenario.name
18
- # @features[feature_location] ||= {}
19
- # @features[feature_location][feature_example] ||= {start_time: Time.now}
20
- # else
21
- # feature_location = scenario.file_colon_line
22
- # feature_name = scenario.name.chomp
23
- # @features[feature_location] ||= {start_time: Time.now}
24
- # end
25
- # end
26
-
27
- # def after_feature(scenario)
28
- # end
29
-
30
10
  def after_features(features)
31
11
  profile = ProfileData.new(@runtime, features)
32
12
  Exporter.new(profile).export
33
13
  end
34
-
35
14
  end
36
-
37
-
38
15
  end
@@ -1,13 +1,15 @@
1
- module CucumberCharacteristics
1
+ require 'pp'
2
2
 
3
+ module CucumberCharacteristics
3
4
  class ProfileData
5
+ CUCUMBER_VERSION = Gem::Version.new(Cucumber::VERSION)
4
6
 
5
7
  extend Forwardable
6
8
 
7
9
  def_delegators :@runtime, :scenarios, :steps
8
10
  attr_reader :duration
9
11
 
10
- STATUS_ORDER = {passed: 0, failed: 2000, skipped: 1000, undefined: 500}
12
+ STATUS_ORDER = { passed: 0, failed: 2000, skipped: 1000, undefined: 500 }.freeze
11
13
 
12
14
  STATUS = STATUS_ORDER.keys
13
15
 
@@ -18,82 +20,64 @@ module CucumberCharacteristics
18
20
  end
19
21
 
20
22
  def ambiguous_count
21
- @runtime.steps.count{|s| ambiguous?(s)}
23
+ @runtime.steps.count { |s| ambiguous?(s) }
22
24
  end
23
25
 
24
26
  def unmatched_steps
25
27
  unmatched = {}
26
28
  @runtime.unmatched_step_definitions.each do |u|
27
- unmatched[u.file_colon_line] = u.regexp_source
29
+ location = u.file_colon_line
30
+ unmatched[location] = u.regexp_source
28
31
  end
29
32
  unmatched.sort
30
33
  end
31
34
 
32
- def has_unmatched_steps?
35
+ def unmatched_steps?
33
36
  unmatched_steps.count > 0
34
37
  end
35
38
 
36
39
  def feature_profiles
37
- feature_profiles = { }
38
- @runtime.scenarios.each do |f|
39
- if f.is_a?(Cucumber::Ast::OutlineTable::ExampleRow)
40
- feature_id = f.scenario_outline.file_colon_line
41
- feature_profiles[feature_id] ||= {name: f.scenario_outline.name, total_duration: 0, step_count: 0, example_count: 0, examples: {} }
42
- example_id = f.name
43
- feature_profiles[feature_id][:examples][example_id] ||= {total_duration: 0, step_count: 0}
44
- feature_profiles[feature_id][:examples][example_id][:total_duration] = f.instance_variable_get(:@step_invocations).select{|s| s.status == :passed}.map{|s| s.step_match.duration}.inject(&:+)
45
- feature_profiles[feature_id][:examples][example_id][:step_count] = f.instance_variable_get(:@step_invocations).count
46
- feature_profiles[feature_id][:examples][example_id][:status] = f.status
47
- else
48
- feature_id = f.file_colon_line
49
- feature_profiles[feature_id] = {name: f.name, total_duration: 0, step_count: 0}
50
- feature_profiles[feature_id][:total_duration] = f.steps.select{|s| s.status == :passed}.map{|s| s.step_match.duration}.inject(&:+)
51
- feature_profiles[feature_id][:step_count] = f.steps.count
52
- feature_profiles[feature_id][:status] = f.status
53
- end
54
- end
55
- with_feature_calculations(feature_profiles)
40
+ return @feature_profiles if @feature_profiles
41
+ feature_profiles = @runtime.scenario_profiles
42
+ @feature_profiles = with_feature_calculations(feature_profiles)
56
43
  end
57
44
 
58
45
  def with_feature_calculations(feature_profiles)
59
46
  feature_profiles.each do |feature, meta|
60
- if meta[:examples]
61
- feature_profiles[feature][:example_count] = meta[:examples].keys.count
62
- feature_profiles[feature][:total_duration] = meta[:examples].map{|e,m| m[:total_duration]}.inject(&:+)
63
- feature_profiles[feature][:step_count] = meta[:examples].map{|e,m| m[:step_count]}.inject(&:+)
64
- feature_profiles[feature][:examples] = feature_profiles[feature][:examples].sort_by{|k, v| v[:total_duration]}.reverse
65
- feature_profiles[feature][:status] = if meta[:examples].all?{|e,m| m[:status] == :passed}
66
- :passed
67
- elsif meta[:examples].any?{|e,m| m[:status] == :failed}
68
- :failed
69
- elsif meta[:examples].any?{|e,m| m[:status] == :skipped}
70
- :skipped
71
- else
72
- :unknown
73
- end
74
- end
47
+ next unless meta[:examples]
48
+ feature_profiles[feature][:example_count] = meta[:examples].keys.count
49
+ feature_profiles[feature][:total_duration] = meta[:examples].map { |_e, m| m[:total_duration] || 0 }.inject(&:+)
50
+ feature_profiles[feature][:step_count] = meta[:examples].map { |_e, m| m[:step_count] }.inject(&:+)
51
+ feature_profiles[feature][:examples] = feature_profiles[feature][:examples].sort_by { |_k, v| v[:total_duration] }.reverse
52
+ feature_profiles[feature][:status] = if meta[:examples].all? { |_e, m| m[:status] == :passed }
53
+ :passed
54
+ elsif meta[:examples].any? { |_e, m| m[:status] == :failed }
55
+ :failed
56
+ elsif meta[:examples].any? { |_e, m| m[:status] == :skipped }
57
+ :skipped
58
+ else
59
+ :unknown
60
+ end
75
61
  end
76
- feature_profiles.sort_by{|k, v| (STATUS_ORDER[v[:status]]||0)+(v[:total_duration] || 0)}.reverse
62
+ feature_profiles.sort_by { |_k, v| (STATUS_ORDER[v[:status]] || 0) + (v[:total_duration] || 0) }.reverse
77
63
  end
78
64
 
79
65
  def step_profiles
80
66
  step_profiles = {}
81
67
  @runtime.steps.each do |s|
82
- unless ambiguous?(s)
83
- step_name = s.status == :undefined ? s.name : s.step_match.step_definition.file_colon_line
84
- # Initialize data structure
85
- step_profiles[step_name] ||= { :total_count => 0}
86
- STATUS.each {|status| step_profiles[step_name][status] ||= {:count => 0, :feature_location => {} }}
87
- feature_location = s.file_colon_line
88
- step_profiles[step_name][s.status][:count] += 1
89
- step_profiles[step_name][:total_count] += 1
90
- step_profiles[step_name][s.status][:feature_location][feature_location] ||= []
91
- if s.status != :undefined
92
- step_profiles[step_name][:regexp] = s.step_match.step_definition.regexp_source
93
- if s.status == :passed
94
- step_profiles[step_name][s.status][:feature_location][feature_location] << s.step_match.duration
95
- end
96
- end
68
+ next if ambiguous?(s)
69
+ step_name = s.status == :undefined ? s.name : s.step_match.step_definition.file_colon_line
70
+ # Initialize data structure
71
+ step_profiles[step_name] ||= { total_count: 0 }
72
+ STATUS.each { |status| step_profiles[step_name][status] ||= { count: 0, feature_location: {} } }
73
+ feature_location = s.file_colon_line
74
+ step_profiles[step_name][s.status][:count] += 1
75
+ step_profiles[step_name][:total_count] += 1
76
+ step_profiles[step_name][s.status][:feature_location][feature_location] ||= []
77
+ next unless s.status != :undefined
78
+ step_profiles[step_name][:regexp] = s.step_match.step_definition.regexp_source
79
+ if s.status == :passed
80
+ step_profiles[step_name][s.status][:feature_location][feature_location] << s.step_match.duration
97
81
  end
98
82
  end
99
83
  with_step_calculations(step_profiles)
@@ -118,18 +102,18 @@ module CucumberCharacteristics
118
102
  step_profiles[step][:variation] = step_profiles[step][:slowest] - step_profiles[step][:fastest]
119
103
  step_profiles[step][:total_duration] = timings.inject(:+)
120
104
  step_profiles[step][:average] = step_profiles[step][:total_duration] / meta[:passed][:count]
121
- sum = timings.inject(0){|accum, i| accum +(i-step_profiles[step][:average])**2 }
122
- step_profiles[step][:variance] = sum/(timings.length ).to_f
123
- step_profiles[step][:standard_deviation] = Math.sqrt( step_profiles[step][:variance])
105
+ sum = timings.inject(0) { |accum, i| accum + (i - step_profiles[step][:average])**2 }
106
+ step_profiles[step][:variance] = sum / timings.length.to_f
107
+ step_profiles[step][:standard_deviation] = Math.sqrt(step_profiles[step][:variance])
124
108
  end
125
- step_profiles.sort_by{|k, v| v[:total_duration]||0}.reverse
109
+ step_profiles.sort_by { |_k, v| v[:total_duration] || 0 }.reverse
126
110
  end
127
111
 
128
112
  def step_duration
129
113
  step_duration = []
130
- step_profiles.each do | step, meta |
114
+ step_profiles.each do |_step, meta|
131
115
  STATUS.each do |status|
132
- meta[status][:feature_location].each do | location, timings |
116
+ meta[status][:feature_location].each do |_location, timings|
133
117
  step_duration << timings
134
118
  end
135
119
  end
@@ -162,7 +146,5 @@ module CucumberCharacteristics
162
146
  end
163
147
  status
164
148
  end
165
-
166
149
  end
167
-
168
150
  end