cuke_sniffer 0.0.3 → 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.
@@ -1,178 +1,238 @@
1
- module CukeSniffer
2
- class Scenario < FeatureRulesEvaluator
3
- include CukeSniffer::Constants
4
- include CukeSniffer::RuleConfig
5
-
6
- xml_accessor :start_line
7
- xml_accessor :steps, :as => [], :in => "steps"
8
- xml_accessor :examples_table, :as => [], :in => "examples"
9
-
10
- attr_accessor :type, :inline_tables
11
-
12
- def initialize(location, scenario)
13
- super(location)
14
- @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
15
- @steps = []
16
- @inline_tables = {}
17
- @examples_table = []
18
- split_scenario(scenario)
19
- evaluate_score
20
- end
21
-
22
- def split_scenario(scenario)
23
- index = 0
24
- until index >= scenario.length or scenario[index] =~ SCENARIO_TITLE_STYLES
25
- update_tag_list(scenario[index])
26
- index += 1
27
- end
28
-
29
- until index >= scenario.length or scenario[index].match STEP_REGEX or scenario[index].include?("Examples:")
30
- match = scenario[index].match(SCENARIO_TITLE_STYLES)
31
- @type = match[:type] unless match.nil?
32
- create_name(scenario[index], SCENARIO_TITLE_STYLES)
33
- index += 1
34
- end
35
-
36
- until index >= scenario.length or scenario[index].include?("Examples:")
37
- if scenario[index] =~ /^\|.*\|/
38
- step = scenario[index - 1]
39
- @inline_tables[step] = []
40
- until index >= scenario.length or scenario[index] =~ STEP_REGEX
41
- @inline_tables[step] << scenario[index]
42
- index += 1
43
- end
44
- else
45
- @steps << scenario[index] if scenario[index] =~ STEP_REGEX
46
- index += 1
47
- end
48
- end
49
-
50
- if index < scenario.length and scenario[index].include?("Examples:")
51
- index += 1
52
- until index >= scenario.length
53
- @examples_table << scenario[index] unless scenario[index].empty?
54
- index += 1
55
- end
56
- end
57
- end
58
-
59
- def ==(comparison_object)
60
- super(comparison_object)
61
- comparison_object.steps == steps
62
- comparison_object.examples_table == examples_table
63
- end
64
-
65
- def get_step_order
66
- order = []
67
- @steps.each { |line|
68
- next if is_comment?(line)
69
- match = line.match(STEP_REGEX)
70
- order << match[:style] unless match.nil?
71
- }
72
- order
73
- end
74
-
75
- def evaluate_score
76
- super
77
- rule_empty_scenario
78
- rule_too_many_steps
79
- rule_step_order
80
- rule_invalid_first_step
81
- rule_asterisk_step
82
- rule_commented_step
83
- rule_implementation_words
84
- rule_date_used_in_step
85
- rule_one_word_step
86
- rule_multiple_given_when_then
87
- evaluate_outline_scores if type == "Scenario Outline"
88
- end
89
-
90
- def rule_multiple_given_when_then
91
- step_order = get_step_order
92
- %w(Given When Then).each { |type| store_rule(RULES[:multiple_given_when_then]) if step_order.count(type) > 1 }
93
- end
94
-
95
- def rule_one_word_step
96
- @steps.each do |step|
97
- store_rule(RULES[:one_word_step]) if step.split.count == 2
98
- end
99
- end
100
-
101
- def evaluate_outline_scores
102
- rule_no_examples_table
103
- rule_no_examples
104
- rule_one_example
105
- rule_too_many_examples
106
- rule_commented_example
107
- end
108
-
109
- def rule_empty_scenario
110
- store_rule(RULES[:no_steps]) if @steps.empty?
111
- end
112
-
113
- def rule_too_many_steps
114
- rule = RULES[:too_many_steps]
115
- store_rule(rule) if @steps.size >= rule[:max]
116
- end
117
-
118
- def rule_step_order
119
- step_order = get_step_order.uniq
120
- %w(But * And).each { |type| step_order.delete(type) }
121
- store_rule(RULES[:out_of_order_steps]) unless step_order == %w(Given When Then) or step_order == %w(When Then)
122
- end
123
-
124
- def rule_invalid_first_step
125
- first_step = get_step_order.first
126
- store_rule(RULES[:invalid_first_step]) if %w(And But).include?(first_step)
127
- end
128
-
129
- def rule_asterisk_step
130
- get_step_order.count('*').times { store_rule(RULES[:asterisk_step]) }
131
- end
132
-
133
- def rule_commented_step
134
- @steps.each do |step|
135
- store_rule(RULES[:commented_step]) if is_comment?(step)
136
- end
137
- end
138
-
139
- def rule_implementation_words
140
- rule = RULES[:implementation_word]
141
- @steps.each do |step|
142
- next if is_comment?(step)
143
- rule[:words].each do |word|
144
- store_updated_rule(rule, rule[:phrase].gsub(/{.*}/, word)) if step.include?(word)
145
- end
146
- end
147
- end
148
-
149
- def rule_date_used_in_step
150
- @steps.each do |step|
151
- store_rule(RULES[:date_used]) if step =~ DATE_REGEX
152
- end
153
- end
154
-
155
- def rule_no_examples_table
156
- store_rule(RULES[:no_examples_table]) if @examples_table.empty?
157
- end
158
-
159
- def rule_no_examples
160
- store_rule(RULES[:no_examples]) if @examples_table.size == 1
161
- end
162
-
163
- def rule_one_example
164
- store_rule(RULES[:one_example]) if @examples_table.size == 2 and !is_comment?(@examples_table[1])
165
- end
166
-
167
- def rule_too_many_examples
168
- store_rule(RULES[:too_many_examples]) if (@examples_table.size - 1) >= 8
169
- end
170
-
171
- def rule_commented_example
172
- @examples_table.each do |example|
173
- store_rule(RULES[:commented_example]) if is_comment?(example)
174
- end
175
- end
176
-
177
- end
178
- end
1
+ module CukeSniffer
2
+
3
+ # Author:: Robert Cochran (mailto:cochrarj@miamioh.edu)
4
+ # Copyright:: Copyright (C) 2013 Robert Cochran
5
+ # License:: Distributes under the MIT License
6
+ # This class is a representation of the cucumber objects
7
+ # Background, Scenario, Scenario Outline
8
+ #
9
+ # Extends CukeSniffer::FeatureRulesEvaluator
10
+ class Scenario < FeatureRulesEvaluator
11
+
12
+ xml_accessor :start_line
13
+ xml_accessor :steps, :as => [], :in => "steps"
14
+ xml_accessor :examples_table, :as => [], :in => "examples"
15
+
16
+ # int: Line on which the scenario begins
17
+ attr_accessor :start_line
18
+
19
+ # string: The type of scenario
20
+ # Background, Scenario, Scenario Outline
21
+ attr_accessor :type
22
+
23
+ # string array: List of each step call in a scenario
24
+ attr_accessor :steps
25
+
26
+ # hash: Keeps each location and content of an inline table
27
+ # * Key: Step string the inline table is attached to
28
+ # * Value: Array of all of the lines in the table
29
+ attr_accessor :inline_tables
30
+
31
+ # string array: List of each example row in a scenario outline
32
+ attr_accessor :examples_table
33
+
34
+ # Location must be in the format of "file_path\file_name.rb:line_number"
35
+ # Scenario must be a string array containing everything from the first tag to the last example table
36
+ # where applicable.
37
+ def initialize(location, scenario)
38
+ super(location)
39
+ @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
40
+ @steps = []
41
+ @inline_tables = {}
42
+ @examples_table = []
43
+ split_scenario(scenario)
44
+ evaluate_score
45
+ end
46
+
47
+ def ==(comparison_object) # :nodoc:
48
+ super(comparison_object) &&
49
+ comparison_object.steps == steps &&
50
+ comparison_object.examples_table == examples_table
51
+ end
52
+
53
+ private
54
+
55
+ def split_scenario(scenario)
56
+ index = 0
57
+ until index >= scenario.length or scenario[index] =~ SCENARIO_TITLE_STYLES
58
+ update_tag_list(scenario[index])
59
+ index += 1
60
+ end
61
+
62
+ until index >= scenario.length or scenario[index].match STEP_REGEX or scenario[index].include?("Examples:")
63
+ match = scenario[index].match(SCENARIO_TITLE_STYLES)
64
+ @type = match[:type] unless match.nil?
65
+ create_name(scenario[index], SCENARIO_TITLE_STYLES)
66
+ index += 1
67
+ end
68
+
69
+ until index >= scenario.length or scenario[index].include?("Examples:")
70
+ if scenario[index] =~ /^\|.*\|/
71
+ step = scenario[index - 1]
72
+ @inline_tables[step] = []
73
+ until index >= scenario.length or scenario[index] =~ /(#{STEP_REGEX}|^\s*Examples:)/
74
+ @inline_tables[step] << scenario[index]
75
+ index += 1
76
+ end
77
+ else
78
+ @steps << scenario[index] if scenario[index] =~ STEP_REGEX
79
+ index += 1
80
+ end
81
+ end
82
+
83
+ if index < scenario.length and scenario[index].include?("Examples:")
84
+ index += 1
85
+ until index >= scenario.length
86
+ index += 2 if scenario[index].include?("Examples:")
87
+ @examples_table << scenario[index] if scenario[index] =~ /#{COMMENT_REGEX}\|.*\|/
88
+ index += 1
89
+ end
90
+ end
91
+ end
92
+
93
+ def get_step_order
94
+ order = []
95
+ @steps.each do |line|
96
+ next if is_comment?(line)
97
+ match = line.match(STEP_REGEX)
98
+ order << match[:style] unless match.nil?
99
+ end
100
+ order
101
+ end
102
+
103
+ def evaluate_score
104
+ if type == "Background"
105
+ rule_numbers_in_name(type)
106
+ rule_long_name(type)
107
+ rule_tagged_background(type)
108
+ else
109
+ super
110
+ rule_step_order
111
+ end
112
+
113
+ rule_empty_scenario
114
+ rule_too_many_steps
115
+ rule_invalid_first_step
116
+ rule_asterisk_step
117
+ rule_commented_step
118
+ rule_implementation_words
119
+ rule_date_used_in_step
120
+ rule_one_word_step
121
+ rule_multiple_given_when_then
122
+ evaluate_outline_scores if type == "Scenario Outline"
123
+ end
124
+
125
+ def evaluate_outline_scores
126
+ rule_no_examples_table
127
+ rule_no_examples
128
+ rule_one_example
129
+ rule_too_many_examples
130
+ rule_commented_example
131
+ end
132
+
133
+ def rule_multiple_given_when_then
134
+ step_order = get_step_order
135
+ rule = RULES[:multiple_given_when_then]
136
+ phrase = rule[:phrase].gsub(/{.*}/, type)
137
+ ["Given", "When", "Then"].each { |step| store_rule(rule, phrase) if step_order.count(step) > 1 }
138
+ end
139
+
140
+ def rule_one_word_step
141
+ @steps.each do |step|
142
+ rule = RULES[:one_word_step]
143
+ store_rule(rule) if step.split.count == 2
144
+ end
145
+ end
146
+
147
+ def rule_step_order
148
+ step_order = get_step_order.uniq
149
+ ["But", "*", "And"].each { |type| step_order.delete(type) }
150
+ rule = RULES[:out_of_order_steps]
151
+ store_rule(rule) unless step_order == %w(Given When Then) or step_order == %w(When Then)
152
+ end
153
+
154
+ def rule_asterisk_step
155
+ get_step_order.count('*').times do
156
+ rule = RULES[:asterisk_step]
157
+ store_rule(rule)
158
+ end
159
+ end
160
+
161
+ def rule_commented_step
162
+ @steps.each do |step|
163
+ rule = RULES[:commented_step]
164
+ store_rule(rule) if is_comment?(step)
165
+ end
166
+ end
167
+
168
+ def rule_date_used_in_step
169
+ @steps.each do |step|
170
+ rule = RULES[:date_used]
171
+ store_rule(rule) if step =~ DATE_REGEX
172
+ end
173
+ end
174
+
175
+ def rule_no_examples_table
176
+ rule = RULES[:no_examples_table]
177
+ store_rule(rule) if @examples_table.empty?
178
+ end
179
+
180
+ def rule_no_examples
181
+ rule = RULES[:no_examples]
182
+ store_rule(rule) if @examples_table.size == 1
183
+ end
184
+
185
+ def rule_one_example
186
+ rule = RULES[:one_example]
187
+ store_rule(rule) if @examples_table.size == 2 and !is_comment?(@examples_table[1])
188
+ end
189
+
190
+ def rule_too_many_examples
191
+ rule = RULES[:too_many_examples]
192
+ store_rule(rule) if (@examples_table.size - 1) >= 8
193
+ end
194
+
195
+ def rule_commented_example
196
+ @examples_table.each do |example|
197
+ rule = RULES[:commented_example]
198
+ store_rule(rule) if is_comment?(example)
199
+ end
200
+ end
201
+
202
+ def rule_implementation_words
203
+ rule = RULES[:implementation_word]
204
+ @steps.each do |step|
205
+ next if is_comment?(step)
206
+ rule[:words].each do |word|
207
+ rule_phrase = rule[:phrase].gsub(/{.*}/, word)
208
+ store_rule(rule, rule_phrase) if step.include?(word)
209
+ end
210
+ end
211
+ end
212
+
213
+ def rule_tagged_background(type)
214
+ rule = RULES[:background_with_tag]
215
+ rule_phrase = rule[:phrase].gsub(/{.*}/, type)
216
+ store_rule(rule, rule_phrase) if tags.size > 0
217
+ end
218
+
219
+ def rule_invalid_first_step
220
+ first_step = get_step_order.first
221
+ rule = RULES[:invalid_first_step]
222
+ rule_phrase = rule[:phrase].gsub(/{.*}/, type)
223
+ store_rule(rule, rule_phrase) if %w(And But).include?(first_step)
224
+ end
225
+
226
+ def rule_empty_scenario
227
+ rule = RULES[:no_steps]
228
+ rule_phrase = rule[:phrase].gsub(/{.*}/, type)
229
+ store_rule(rule, rule_phrase) if @steps.empty?
230
+ end
231
+
232
+ def rule_too_many_steps
233
+ rule = RULES[:too_many_steps]
234
+ rule_phrase = rule[:phrase].gsub(/{.*}/, type)
235
+ store_rule(rule, rule_phrase) if @steps.size >= rule[:max]
236
+ end
237
+ end
238
+ end
@@ -1,202 +1,239 @@
1
- require 'roxml'
2
- module CukeSniffer
3
- class StepDefinition < RulesEvaluator
4
- include CukeSniffer::Constants
5
- include CukeSniffer::RuleConfig
6
-
7
- xml_accessor :start_line
8
- xml_accessor :regex
9
- xml_accessor :parameters, :as => [], :in => "parameters"
10
- xml_accessor :nested_steps, :as => {:key => 'location', :value => 'call'}, :in => "nested_steps"
11
- xml_accessor :calls, :as => {:key => 'location', :value => 'call'}, :in => "calls"
12
- xml_accessor :code, :as => [], :in => "code"
13
-
14
- SIMPLE_NESTED_STEP_REGEX = /steps\s"#{STEP_STYLES}(?<step_string>.*)"$/
15
- START_COMPLEX_STEP_REGEX = /^steps\s%(q|Q)?\{\s*/
16
- SAME_LINE_COMPLEX_STEP_REGEX = /#{START_COMPLEX_STEP_REGEX}#{STEP_STYLES}(?<step_string>.*)}$/
17
- END_COMPLEX_STEP_REGEX = /}$/
18
- START_COMPLEX_WITH_STEP_REGEX = /steps\s%(q|Q)?\{#{STEP_STYLES}(?<step_string>.*)$/
19
- END_COMPLEX_WITH_STEP_REGEX = /#{STEP_STYLES}(?<step_string>.*)}$/
20
-
21
- def initialize(location, raw_code)
22
- super(location)
23
-
24
- @parameters = []
25
- @calls = {}
26
- @nested_steps = {}
27
- @start_line = location.match(/:(?<line>\d*)$/)[:line].to_i
28
-
29
- end_match_index = (raw_code.size - 1) - raw_code.reverse.index("end")
30
- @code = raw_code[1...end_match_index]
31
-
32
- raw_code.each do |line|
33
- if line =~ STEP_DEFINITION_REGEX
34
- matches = STEP_DEFINITION_REGEX.match(line)
35
- @regex = Regexp.new(matches[:step])
36
- @parameters = matches[:parameters].split(",") unless matches[:parameters].nil?
37
- end
38
- end
39
-
40
- detect_nested_steps
41
- evaluate_score
42
- end
43
-
44
- def add_call(location, step_string)
45
- @calls[location] = step_string
46
- end
47
-
48
- def detect_nested_steps
49
- multi_line_step_flag = false
50
- counter = 1
51
- @code.each do |line|
52
- regex = nil
53
- case line
54
- when SIMPLE_NESTED_STEP_REGEX
55
- regex = SIMPLE_NESTED_STEP_REGEX
56
- when START_COMPLEX_WITH_STEP_REGEX
57
- if line =~ /\}$/
58
- if line.include?('#{')
59
- reversed_line = line.reverse
60
- last_capture = reversed_line[0..reversed_line.index('#')].reverse
61
- if last_capture =~ /{.*}$/
62
- multi_line_step_flag = true
63
- regex = START_COMPLEX_WITH_STEP_REGEX
64
- else
65
- regex = SAME_LINE_COMPLEX_STEP_REGEX
66
- end
67
- else
68
- regex = SAME_LINE_COMPLEX_STEP_REGEX
69
- end
70
- else
71
- multi_line_step_flag = true
72
- regex = START_COMPLEX_WITH_STEP_REGEX
73
- end
74
- when SAME_LINE_COMPLEX_STEP_REGEX
75
- regex = SAME_LINE_COMPLEX_STEP_REGEX
76
- when END_COMPLEX_WITH_STEP_REGEX
77
- if line =~ /[#]{.*}$/ && multi_line_step_flag
78
- regex = STEP_REGEX
79
- else
80
- regex = END_COMPLEX_WITH_STEP_REGEX
81
- multi_line_step_flag = false
82
- end
83
- when START_COMPLEX_STEP_REGEX
84
- multi_line_step_flag = true
85
- when STEP_REGEX
86
- regex = STEP_REGEX if multi_line_step_flag
87
- when END_COMPLEX_STEP_REGEX
88
- multi_line_step_flag = false
89
- else
90
- end
91
-
92
- if regex
93
- index = 0
94
- while line.include?('#{') and index <= line.length
95
- index = line.index('#{')
96
- replace_string = ""
97
- while index <= line.length and line[index - 1] != "}"
98
- replace_string << line[index]
99
- index += 1
100
- end
101
- line.gsub!(replace_string, "variable")
102
- end
103
-
104
- match = regex.match(line)
105
- nested_step_line = (@start_line + counter)
106
- @nested_steps[location.gsub(/:\d*/, ":" + nested_step_line.to_s)] = match[:step_string]
107
- end
108
- counter += 1
109
- end
110
- end
111
-
112
- def condensed_call_list
113
- condensed_list = {}
114
- @calls.each { |call, step_string|
115
- condensed_list[step_string] ||= []
116
- condensed_list[step_string] << call
117
- }
118
- condensed_list
119
- end
120
-
121
- def ==(comparison_object)
122
- super(comparison_object)
123
- comparison_object.regex == regex
124
- comparison_object.code == code
125
- comparison_object.parameters == parameters
126
- comparison_object.calls == calls
127
- comparison_object.nested_steps == nested_steps
128
- end
129
-
130
- def evaluate_score
131
- super
132
- rule_no_code
133
- rule_too_many_parameters
134
- rule_nested_steps
135
- rule_recursive_nested_step
136
- rule_commented_code
137
- rule_lazy_debugging
138
- rule_pending
139
- rule_todo
140
- sleep_rules
141
- end
142
-
143
- def rule_todo
144
- code.each do |line|
145
- store_rule(RULES[:todo]) if line =~ /\#(TODO|todo)/
146
- end
147
- end
148
-
149
- def sleep_rules
150
- code.each do |line|
151
- match_data = line.match /^\s*sleep(\s|\()(?<sleep_time>.*)\)?/
152
- unless match_data.nil?
153
- sleep_value = match_data[:sleep_time].to_f
154
- store_rule(RULES[:small_sleep]) if sleep_value <= RULES[:small_sleep][:max]
155
- store_rule(RULES[:large_sleep]) if sleep_value > RULES[:large_sleep][:min]
156
- end
157
- end
158
- end
159
-
160
- def rule_pending
161
- code.each do |line|
162
- if line =~ /^\s*pending\s*$/
163
- store_rule(RULES[:pending])
164
- return
165
- end
166
- end
167
- end
168
-
169
- def rule_lazy_debugging
170
- code.each do |line|
171
- next if is_comment?(line)
172
- store_rule(RULES[:lazy_debugging]) if line.strip =~ /^(p|puts)( |\()('|"|%(q|Q)?\{)/
173
- end
174
- end
175
-
176
- def rule_no_code
177
- store_rule(RULES[:no_code]) if code.empty?
178
- end
179
-
180
- def rule_too_many_parameters
181
- rule = RULES[:too_many_parameters]
182
- store_rule(rule) if parameters.size >= rule[:max]
183
- end
184
-
185
- def rule_nested_steps
186
- store_rule(RULES[:nested_step]) unless nested_steps.empty?
187
- end
188
-
189
- def rule_recursive_nested_step
190
- nested_steps.each_value do |nested_step|
191
- store_rule(RULES[:recursive_nested_step]) if nested_step =~ regex
192
- end
193
- end
194
-
195
- def rule_commented_code
196
- code.each do |line|
197
- store_rule(RULES[:commented_code]) if is_comment?(line)
198
- end
199
- end
200
-
201
- end
202
- 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 < 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