cucumber_characteristics 0.0.4 → 0.0.5

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.
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