cuke_sniffer 0.0.5 → 0.0.6

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 (31) hide show
  1. data/bin/cuke_sniffer +85 -63
  2. data/lib/cuke_sniffer.rb +23 -16
  3. data/lib/cuke_sniffer/cli.rb +269 -523
  4. data/lib/cuke_sniffer/constants.rb +5 -2
  5. data/lib/cuke_sniffer/cuke_sniffer_helper.rb +165 -0
  6. data/lib/cuke_sniffer/dead_steps_helper.rb +54 -0
  7. data/lib/cuke_sniffer/feature.rb +13 -81
  8. data/lib/cuke_sniffer/feature_rules_evaluator.rb +56 -115
  9. data/lib/cuke_sniffer/formatter.rb +142 -0
  10. data/lib/cuke_sniffer/hook.rb +77 -160
  11. data/lib/cuke_sniffer/report/dead_steps.html.erb +36 -0
  12. data/lib/cuke_sniffer/report/features.html.erb +60 -0
  13. data/lib/cuke_sniffer/report/hooks.html.erb +49 -0
  14. data/lib/cuke_sniffer/report/improvement_list.html.erb +18 -0
  15. data/lib/cuke_sniffer/report/legend.html.erb +167 -0
  16. data/lib/cuke_sniffer/report/min_template.html.erb +125 -0
  17. data/lib/cuke_sniffer/report/rules.html.erb +9 -0
  18. data/lib/cuke_sniffer/report/standard_template.html.erb +137 -0
  19. data/lib/cuke_sniffer/report/step_definitions.html.erb +49 -0
  20. data/lib/cuke_sniffer/report/sub_rules.html.erb +24 -0
  21. data/lib/cuke_sniffer/report/summary.html.erb +71 -0
  22. data/lib/cuke_sniffer/rule.rb +28 -0
  23. data/lib/cuke_sniffer/rule_config.rb +241 -48
  24. data/lib/cuke_sniffer/rule_target.rb +62 -0
  25. data/lib/cuke_sniffer/rules_evaluator.rb +65 -70
  26. data/lib/cuke_sniffer/scenario.rb +102 -238
  27. data/lib/cuke_sniffer/step_definition.rb +163 -239
  28. data/lib/cuke_sniffer/summary_helper.rb +91 -0
  29. data/lib/cuke_sniffer/summary_node.rb +19 -0
  30. metadata +23 -5
  31. data/lib/cuke_sniffer/report/markup.rhtml +0 -353
@@ -1,239 +1,163 @@
1
- require 'roxml'
2
- module CukeSniffer
3
-
4
- # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
5
- # Copyright:: Copyright (C) 2013 Robert Cochran
6
- # License:: Distributes under the MIT License
7
- # Translates and evaluates Cucumber step definitions
8
- # Extends CukeSniffer::RulesEvaluator
9
- class StepDefinition < RulesEvaluator
10
-
11
- xml_accessor :start_line
12
- xml_accessor :regex
13
- xml_accessor :parameters, :as => [], :in => "parameters"
14
- xml_accessor :nested_steps, :as => {:key => 'location', :value => 'call'}, :in => "nested_steps"
15
- xml_accessor :calls, :as => {:key => 'location', :value => 'call'}, :in => "calls"
16
- xml_accessor :code, :as => [], :in => "code"
17
-
18
- # int: Line on which a step definition starts
19
- attr_accessor :start_line
20
-
21
- # Regex: Regex that cucumber uses to match step calls
22
- attr_accessor :regex
23
-
24
- # string array: List of the parameters a step definition has
25
- attr_accessor :parameters
26
-
27
- # hash: Contains each nested step call a step definition has
28
- # * Key: location:line of the nested step
29
- # * Value: The step call that appears on the line
30
- attr_accessor :nested_steps
31
-
32
- # hash: Contains each call that is made to a step definition
33
- # * Key: Location in which the step definition is called from
34
- # * Value: The step string that matched the regex
35
- # In the case of a fuzzy match it will be a regex of the
36
- # step call that was the inverse match of the regex translated
37
- # into a string.
38
- attr_accessor :calls
39
-
40
- # string array: List of all of the content between the regex and the end of the step definition.
41
- attr_accessor :code
42
-
43
- # location must be in the format of "file_path\file_name.rb:line_number"
44
- # raw_code is an array of strings that represents the step definition
45
- # must contain the regex line and its pairing end
46
- def initialize(location, raw_code)
47
- super(location)
48
-
49
- @parameters = []
50
- @calls = {}
51
- @nested_steps = {}
52
- @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
53
-
54
- end_match_index = (raw_code.size - 1) - raw_code.reverse.index("end")
55
- @code = raw_code[1...end_match_index]
56
-
57
- raw_code.each do |line|
58
- if line =~ STEP_DEFINITION_REGEX
59
- matches = STEP_DEFINITION_REGEX.match(line)
60
- @regex = Regexp.new(matches[:step])
61
- @parameters = matches[:parameters].split(/,\s*/) if matches[:parameters]
62
- end
63
- end
64
-
65
- detect_nested_steps
66
- evaluate_score
67
- end
68
-
69
- # Adds new location => step_string pairs to the calls hash
70
- def add_call(location, step_string)
71
- @calls[location] = step_string
72
- end
73
-
74
- def ==(comparison_object) # :nodoc:
75
- super(comparison_object) &&
76
- comparison_object.regex == regex &&
77
- comparison_object.code == code &&
78
- comparison_object.parameters == parameters &&
79
- comparison_object.calls == calls &&
80
- comparison_object.nested_steps == nested_steps
81
- end
82
-
83
- def condensed_call_list
84
- condensed_list = {}
85
- @calls.each do |call, step_string|
86
- condensed_list[step_string] ||= []
87
- condensed_list[step_string] << call
88
- end
89
- condensed_list
90
- end
91
-
92
- private
93
-
94
- SIMPLE_NESTED_STEP_REGEX = /steps?\s"#{STEP_STYLES}(?<step_string>.*)"$/ # :nodoc:
95
- START_COMPLEX_STEP_REGEX = /^steps?\s%(q|Q)?\{\s*/ # :nodoc:
96
- SAME_LINE_COMPLEX_STEP_REGEX = /#{START_COMPLEX_STEP_REGEX}#{STEP_STYLES}(?<step_string>.*)}$/ # :nodoc:
97
- END_COMPLEX_STEP_REGEX = /}$/ # :nodoc:
98
- START_COMPLEX_WITH_STEP_REGEX = /#{START_COMPLEX_STEP_REGEX}#{STEP_STYLES}(?<step_string>.*)$/ # :nodoc:
99
- END_COMPLEX_WITH_STEP_REGEX = /#{STEP_STYLES}(?<step_string>.*)}$/ # :nodoc:
100
-
101
- def detect_nested_steps
102
- multi_line_step_flag = false
103
- counter = 1
104
- @code.each do |line|
105
- regex = nil
106
- case line
107
- when SIMPLE_NESTED_STEP_REGEX
108
- regex = SIMPLE_NESTED_STEP_REGEX
109
- when SAME_LINE_COMPLEX_STEP_REGEX
110
- regex = SAME_LINE_COMPLEX_STEP_REGEX
111
- when START_COMPLEX_WITH_STEP_REGEX
112
- if line =~ /\}$/
113
- if line.include?('#{')
114
- reversed_line = line.reverse
115
- last_capture = reversed_line[0..reversed_line.index('#')].reverse
116
- if last_capture =~ /{.*}$/
117
- multi_line_step_flag = true
118
- regex = START_COMPLEX_WITH_STEP_REGEX
119
- else
120
- regex = SAME_LINE_COMPLEX_STEP_REGEX
121
- end
122
- else
123
- regex = SAME_LINE_COMPLEX_STEP_REGEX
124
- end
125
- else
126
- multi_line_step_flag = true
127
- regex = START_COMPLEX_WITH_STEP_REGEX
128
- end
129
- when END_COMPLEX_WITH_STEP_REGEX
130
- if line =~ /[#]{.*}$/ && multi_line_step_flag
131
- regex = STEP_REGEX
132
- else
133
- regex = END_COMPLEX_WITH_STEP_REGEX
134
- multi_line_step_flag = false
135
- end
136
- when START_COMPLEX_STEP_REGEX
137
- multi_line_step_flag = true
138
- when STEP_REGEX
139
- regex = STEP_REGEX if multi_line_step_flag
140
- when END_COMPLEX_STEP_REGEX
141
- multi_line_step_flag = false
142
- else
143
- end
144
-
145
- if regex and !is_comment?(line)
146
- match = regex.match(line)
147
- nested_step_line = (@start_line + counter)
148
- @nested_steps[location.gsub(/:\d*$/, ":" + nested_step_line.to_s)] = match[:step_string].gsub("\\", "")
149
- end
150
- counter += 1
151
- end
152
- end
153
-
154
- def evaluate_score
155
- rule_no_code
156
- rule_too_many_parameters
157
- rule_nested_steps
158
- rule_recursive_nested_step
159
- rule_commented_code
160
- rule_lazy_debugging
161
- rule_pending
162
- rule_todo
163
- sleep_rules
164
- end
165
-
166
- def rule_todo
167
- code.each do |line|
168
- rule = RULES[:todo]
169
- store_rule(rule) if line =~ /\#(TODO|todo)/
170
- end
171
- end
172
-
173
- def sleep_rules
174
- code.each do |line|
175
- match_data = line.match /^\s*sleep(\s|\()(?<sleep_time>.*)\)?/
176
- if match_data
177
- sleep_value = match_data[:sleep_time].to_f
178
- rule_small_sleep(sleep_value)
179
- rule_large_sleep(sleep_value)
180
- end
181
- end
182
- end
183
-
184
- def rule_large_sleep(sleep_value)
185
- rule = RULES[:large_sleep]
186
- store_rule(rule) if sleep_value > rule[:min]
187
- end
188
-
189
- def rule_small_sleep(sleep_value)
190
- rule = RULES[:small_sleep]
191
- store_rule(rule) if sleep_value <= rule[:max]
192
- end
193
-
194
- def rule_pending
195
- rule = RULES[:pending]
196
- code.each do |line|
197
- store_rule(rule) if line =~ /^\s*pending\s*$/
198
- return
199
- end
200
- end
201
-
202
- def rule_lazy_debugging
203
- rule = RULES[:lazy_debugging]
204
- code.each do |line|
205
- next if is_comment?(line)
206
- store_rule(rule) if line.strip =~ /^(p|puts)( |\()('|"|%(q|Q)?\{)/
207
- end
208
- end
209
-
210
- def rule_no_code
211
- rule = RULES[:no_code]
212
- store_rule(rule) if code.empty?
213
- end
214
-
215
- def rule_too_many_parameters
216
- rule = RULES[:too_many_parameters]
217
- store_rule(rule) if parameters.size >= rule[:max]
218
- end
219
-
220
- def rule_nested_steps
221
- rule = RULES[:nested_step]
222
- store_rule(rule) unless nested_steps.empty?
223
- end
224
-
225
- def rule_recursive_nested_step
226
- rule = RULES[:recursive_nested_step]
227
- nested_steps.each_value do |nested_step|
228
- store_rule(rule) if nested_step =~ regex
229
- end
230
- end
231
-
232
- def rule_commented_code
233
- rule = RULES[:commented_code]
234
- code.each do |line|
235
- store_rule(rule) if is_comment?(line)
236
- end
237
- end
238
- end
239
- end
1
+ require 'roxml'
2
+ module CukeSniffer
3
+
4
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
5
+ # Copyright:: Copyright (C) 2013 Robert Cochran
6
+ # License:: Distributes under the MIT License
7
+ # Translates and evaluates Cucumber step definitions
8
+ # Extends CukeSniffer::RulesEvaluator
9
+ class StepDefinition < RuleTarget
10
+
11
+ xml_accessor :start_line
12
+ xml_accessor :regex
13
+ xml_accessor :parameters, :as => [], :in => "parameters"
14
+ xml_accessor :nested_steps, :as => {:key => 'location', :value => 'call'}, :in => "nested_steps"
15
+ xml_accessor :calls, :as => {:key => 'location', :value => 'call'}, :in => "calls"
16
+ xml_accessor :code, :as => [], :in => "code"
17
+
18
+ # int: Line on which a step definition starts
19
+ attr_accessor :start_line
20
+
21
+ # Regex: Regex that cucumber uses to match step calls
22
+ attr_accessor :regex
23
+
24
+ # string array: List of the parameters a step definition has
25
+ attr_accessor :parameters
26
+
27
+ # hash: Contains each nested step call a step definition has
28
+ # * Key: location:line of the nested step
29
+ # * Value: The step call that appears on the line
30
+ attr_accessor :nested_steps
31
+
32
+ # hash: Contains each call that is made to a step definition
33
+ # * Key: Location in which the step definition is called from
34
+ # * Value: The step string that matched the regex
35
+ # In the case of a fuzzy match it will be a regex of the
36
+ # step call that was the inverse match of the regex translated
37
+ # into a string.
38
+ attr_accessor :calls
39
+
40
+ # string array: List of all of the content between the regex and the end of the step definition.
41
+ attr_accessor :code
42
+
43
+ # location must be in the format of "file_path\file_name.rb:line_number"
44
+ # step_definition_block is an array of strings that represents the step definition
45
+ # must contain the regex line and its pairing end
46
+ def initialize(location, step_definition_block)
47
+ @parameters = []
48
+ @calls = {}
49
+ @nested_steps = {}
50
+ super(location)
51
+ extract_start_line(location)
52
+ extract_code(step_definition_block)
53
+ extract_step_definition_signature(step_definition_block)
54
+
55
+ detect_nested_steps
56
+ end
57
+
58
+ # Adds new location => step_string pairs to the calls hash
59
+ def add_call(location, step_string)
60
+ @calls[location] = step_string
61
+ end
62
+
63
+ def ==(comparison_object) # :nodoc:
64
+ comparison_object.regex == regex && comparison_object.parameters == parameters
65
+ end
66
+
67
+ def condensed_call_list
68
+ condensed_list = {}
69
+ @calls.each do |call, step_string|
70
+ condensed_list[step_string] ||= []
71
+ condensed_list[step_string] << call
72
+ end
73
+ condensed_list
74
+ end
75
+
76
+ private
77
+
78
+ SIMPLE_NESTED_STEP_REGEX = /steps?\s"#{STEP_STYLES}(?<step_string>.*)"$/ # :nodoc:
79
+ START_COMPLEX_STEP_REGEX = /^steps?\s%(q|Q)?\{\s*/ # :nodoc:
80
+ SAME_LINE_COMPLEX_STEP_REGEX = /#{START_COMPLEX_STEP_REGEX}#{STEP_STYLES}(?<step_string>.*)}$/ # :nodoc:
81
+ END_COMPLEX_STEP_REGEX = /}$/ # :nodoc:
82
+ START_COMPLEX_WITH_STEP_REGEX = /#{START_COMPLEX_STEP_REGEX}#{STEP_STYLES}(?<step_string>.*)$/ # :nodoc:
83
+ END_COMPLEX_WITH_STEP_REGEX = /#{STEP_STYLES}(?<step_string>.*)}$/ # :nodoc:
84
+
85
+ def detect_nested_steps
86
+ multi_line_step_flag = false
87
+ counter = 1
88
+ @code.each do |line|
89
+ regex = nil
90
+ case line
91
+ when SIMPLE_NESTED_STEP_REGEX
92
+ regex = SIMPLE_NESTED_STEP_REGEX
93
+ when SAME_LINE_COMPLEX_STEP_REGEX
94
+ regex = SAME_LINE_COMPLEX_STEP_REGEX
95
+ when START_COMPLEX_WITH_STEP_REGEX
96
+ if line =~ /\}$/
97
+ if line.include?('#{')
98
+ reversed_line = line.reverse
99
+ last_capture = reversed_line[0..reversed_line.index('#')].reverse
100
+ if last_capture =~ /{.*}$/
101
+ multi_line_step_flag = true
102
+ regex = START_COMPLEX_WITH_STEP_REGEX
103
+ else
104
+ regex = SAME_LINE_COMPLEX_STEP_REGEX
105
+ end
106
+ else
107
+ regex = SAME_LINE_COMPLEX_STEP_REGEX
108
+ end
109
+ else
110
+ multi_line_step_flag = true
111
+ regex = START_COMPLEX_WITH_STEP_REGEX
112
+ end
113
+ when END_COMPLEX_WITH_STEP_REGEX
114
+ if line =~ /[#]{.*}$/ && multi_line_step_flag
115
+ regex = STEP_REGEX
116
+ else
117
+ regex = END_COMPLEX_WITH_STEP_REGEX
118
+ multi_line_step_flag = false
119
+ end
120
+ when START_COMPLEX_STEP_REGEX
121
+ multi_line_step_flag = true
122
+ when STEP_REGEX
123
+ regex = STEP_REGEX if multi_line_step_flag
124
+ when END_COMPLEX_STEP_REGEX
125
+ multi_line_step_flag = false
126
+ else
127
+ end
128
+
129
+ if regex and !is_comment?(line)
130
+ match = regex.match(line)
131
+ nested_step_line = (@start_line + counter)
132
+ @nested_steps[location.gsub(/:\d*$/, ":" + nested_step_line.to_s)] = match[:step_string].gsub("\\", "")
133
+ end
134
+ counter += 1
135
+ end
136
+ end
137
+
138
+ def extract_step_definition_signature(step_definition_block)
139
+ regex_line = find_regex_line(step_definition_block)
140
+ unless regex_line.nil?
141
+ matches = STEP_DEFINITION_REGEX.match(regex_line)
142
+ @regex = Regexp.new(matches[:step])
143
+ @parameters = matches[:parameters].split(/,\s*/).collect { |param| param.strip } if matches[:parameters]
144
+ end
145
+ end
146
+
147
+ def extract_start_line(location)
148
+ @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
149
+ end
150
+
151
+ def extract_code(step_definition_block)
152
+ end_match_index = (step_definition_block.size - 1) - step_definition_block.reverse.index("end")
153
+ @code = step_definition_block[1...end_match_index]
154
+ end
155
+
156
+ def find_regex_line(step_definition_block)
157
+ step_definition_block.each do |line|
158
+ return line if line =~ STEP_DEFINITION_REGEX
159
+ end
160
+ nil
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,91 @@
1
+ module CukeSniffer
2
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
3
+ # Copyright:: Copyright (C) 2013 Robert Cochran
4
+ # License:: Distributes under the MIT License
5
+ # Mixins: CukeSniffer::Constants
6
+ # A static class used to help with handling summary data for CukeSniffer::CLI
7
+ class SummaryHelper
8
+ include CukeSniffer::Constants
9
+
10
+ # Sorts the list of improvements in descending order of times found.
11
+ def self.sort_improvement_list(improvement_list)
12
+ sorted_array = improvement_list.sort_by { |improvement, occurrence| occurrence }.reverse
13
+ sorted_improvement_list = {}
14
+ sorted_array.each { |node|
15
+ sorted_improvement_list[node[0]] = node[1]
16
+ }
17
+ sorted_improvement_list
18
+ end
19
+
20
+ # Builds a default assessment hash.
21
+ def self.make_assessment_hash
22
+ {
23
+ :total => 0,
24
+ :total_score => 0,
25
+ :min => nil,
26
+ :min_file => nil,
27
+ :max => nil,
28
+ :max_file => nil,
29
+ :average => 0,
30
+ :threshold => nil,
31
+ :good => 0,
32
+ :bad => 0,
33
+ :improvement_list => {}
34
+ }
35
+ end
36
+
37
+ # Initializes an assessment hash for the rule target list
38
+ def self.initialize_assessment_hash(rule_target_list, type)
39
+ assessment_hash = make_assessment_hash
40
+ assessment_hash[:total] = rule_target_list.count
41
+ assessment_hash[:threshold] = THRESHOLDS[type]
42
+
43
+ unless rule_target_list.empty?
44
+ score = rule_target_list.first.score
45
+ location = rule_target_list.first.location
46
+ assessment_hash[:min] = score
47
+ assessment_hash[:min_file] = location
48
+ assessment_hash[:max] = score
49
+ assessment_hash[:max_file] = location
50
+ end
51
+ assessment_hash
52
+ end
53
+
54
+ # Returns a summary hash for the rule_target_list
55
+ def self.assess_rule_target_list(rule_target_list, type)
56
+ assessment_hash = initialize_assessment_hash(rule_target_list, type)
57
+ rule_target_list.each do |rule_target|
58
+ score = rule_target.score
59
+ assessment_hash[:total_score] += score
60
+ assessment_hash[rule_target.good? ? :good : :bad] += 1
61
+ if score < assessment_hash[:min]
62
+ assessment_hash[:min] = score
63
+ assessment_hash[:min_file] = rule_target.location
64
+ end
65
+ if score > assessment_hash[:max]
66
+ assessment_hash[:max] = score
67
+ assessment_hash[:max_file] = rule_target.location
68
+ end
69
+ rule_target.rules_hash.each_key do |key|
70
+ assessment_hash[:improvement_list][key] ||= 0
71
+ assessment_hash[:improvement_list][key] += rule_target.rules_hash[key]
72
+ end
73
+ end
74
+ assessment_hash[:average] = (assessment_hash[:total_score].to_f/rule_target_list.count.to_f).round(2)
75
+ assessment_hash
76
+ end
77
+
78
+ # Returns a CukeSniffer::SummaryNode object for the passed hash
79
+ def self.load_summary_data(summary_hash)
80
+ summary_node = SummaryNode.new
81
+ summary_node.count = summary_hash[:total]
82
+ summary_node.score = summary_hash[:total_score]
83
+ summary_node.average = summary_hash[:average]
84
+ summary_node.threshold = summary_hash[:threshold]
85
+ summary_node.good = summary_hash[:good]
86
+ summary_node.bad = summary_hash[:bad]
87
+ summary_node
88
+ end
89
+
90
+ end
91
+ end