cuke_sniffer 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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