cuke_sniffer 0.0.1 → 0.0.2
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.
- data/bin/cuke_sniffer.rb +51 -34
- data/lib/cuke_sniffer.rb +13 -13
- data/lib/cuke_sniffer/cli.rb +246 -226
- data/lib/cuke_sniffer/constants.rb +22 -35
- data/lib/cuke_sniffer/feature.rb +107 -116
- data/lib/cuke_sniffer/report/markup.rhtml +385 -310
- data/lib/cuke_sniffer/rule_config.rb +161 -161
- data/lib/cuke_sniffer/rules_evaluator.rb +50 -41
- data/lib/cuke_sniffer/scenario.rb +164 -163
- data/lib/cuke_sniffer/step_definition.rb +143 -116
- metadata +10 -4
data/bin/cuke_sniffer.rb
CHANGED
@@ -1,34 +1,51 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'cuke_sniffer'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'cuke_sniffer'
|
3
|
+
|
4
|
+
help_cmd_txt = "Welcome to CukeSniffer!
|
5
|
+
Calling CukeSniffer with no arguments will run it against the current directory.
|
6
|
+
Other Options for Running include:
|
7
|
+
<feature_file_path>, <step_def_file_path> : Runs CukeSniffer against the
|
8
|
+
specified paths.
|
9
|
+
-o, --out html (name) : Runs CukeSniffer then outputs an
|
10
|
+
html file in the current
|
11
|
+
directory (with optional name).
|
12
|
+
-h, --help : You get this lovely document."
|
13
|
+
|
14
|
+
|
15
|
+
if ARGV.include? "-h" or ARGV.include? "--help"
|
16
|
+
puts help_cmd_txt
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
cuke_sniffer = nil
|
21
|
+
if (ARGV[0] != nil and File.directory?(ARGV[0])) and (ARGV[1] != nil and File.directory?(ARGV[1]))
|
22
|
+
cuke_sniffer = CukeSniffer::CLI.new(ARGV[0], ARGV[1])
|
23
|
+
else
|
24
|
+
cuke_sniffer = CukeSniffer::CLI.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def print_results(cuke_sniffer)
|
28
|
+
puts cuke_sniffer.output_results
|
29
|
+
end
|
30
|
+
|
31
|
+
if ARGV.include? "--out" or ARGV.include? "-o"
|
32
|
+
index = ARGV.index("--out")
|
33
|
+
index ||= ARGV.index("-o")
|
34
|
+
out_type = ARGV[index + 1]
|
35
|
+
case out_type
|
36
|
+
when "html"
|
37
|
+
file_name = ARGV[index + 2]
|
38
|
+
if file_name.nil?
|
39
|
+
cuke_sniffer.output_html
|
40
|
+
else
|
41
|
+
file_name = file_name + ".html" unless file_name =~ /\.html$/
|
42
|
+
cuke_sniffer.output_html(file_name)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
print_results(cuke_sniffer)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
print_results(cuke_sniffer)
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "Completed Sniffing."
|
data/lib/cuke_sniffer.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
|
2
|
-
require 'cuke_sniffer/constants'
|
3
|
-
require 'cuke_sniffer/rule_config'
|
4
|
-
require 'cuke_sniffer/rules_evaluator'
|
5
|
-
require 'cuke_sniffer/feature_rules_evaluator'
|
6
|
-
require 'cuke_sniffer/feature'
|
7
|
-
require 'cuke_sniffer/scenario'
|
8
|
-
require 'cuke_sniffer/step_definition'
|
9
|
-
require 'cuke_sniffer/cli'
|
10
|
-
|
11
|
-
module CukeSniffer
|
12
|
-
|
13
|
-
end
|
1
|
+
|
2
|
+
require 'cuke_sniffer/constants'
|
3
|
+
require 'cuke_sniffer/rule_config'
|
4
|
+
require 'cuke_sniffer/rules_evaluator'
|
5
|
+
require 'cuke_sniffer/feature_rules_evaluator'
|
6
|
+
require 'cuke_sniffer/feature'
|
7
|
+
require 'cuke_sniffer/scenario'
|
8
|
+
require 'cuke_sniffer/step_definition'
|
9
|
+
require 'cuke_sniffer/cli'
|
10
|
+
|
11
|
+
module CukeSniffer
|
12
|
+
|
13
|
+
end
|
data/lib/cuke_sniffer/cli.rb
CHANGED
@@ -1,226 +1,246 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@step_definitions = []
|
15
|
-
|
16
|
-
puts "\nFeatures:"
|
17
|
-
unless features_location.nil?
|
18
|
-
if File.file?(features_location)
|
19
|
-
@features = [Feature.new(features_location)]
|
20
|
-
else
|
21
|
-
build_file_list_from_folder(features_location, ".feature").each { |location|
|
22
|
-
@features << Feature.new(location)
|
23
|
-
print '.'
|
24
|
-
}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
puts "\
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
:
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
|
154
|
-
def
|
155
|
-
|
156
|
-
@
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
def
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
end
|
225
|
-
|
226
|
-
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module CukeSniffer
|
4
|
+
class CLI
|
5
|
+
include CukeSniffer::Constants
|
6
|
+
|
7
|
+
attr_accessor :features_location,:step_definitions_location, :features, :scenarios, :step_definitions, :summary
|
8
|
+
|
9
|
+
def initialize(features_location = Dir.getwd, step_definitions_location = Dir.getwd)
|
10
|
+
@features_location = features_location
|
11
|
+
@step_definitions_location = step_definitions_location
|
12
|
+
@features = []
|
13
|
+
@scenarios = []
|
14
|
+
@step_definitions = []
|
15
|
+
|
16
|
+
puts "\nFeatures:"
|
17
|
+
unless features_location.nil?
|
18
|
+
if File.file?(features_location)
|
19
|
+
@features = [CukeSniffer::Feature.new(features_location)]
|
20
|
+
else
|
21
|
+
build_file_list_from_folder(features_location, ".feature").each { |location|
|
22
|
+
@features << CukeSniffer::Feature.new(location)
|
23
|
+
print '.'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@scenarios = get_all_scenarios(@features)
|
29
|
+
|
30
|
+
puts("\nStep Definitions:")
|
31
|
+
unless step_definitions_location.nil?
|
32
|
+
if File.file?(step_definitions_location)
|
33
|
+
@step_definitions = [build_step_definitions(step_definitions_location)]
|
34
|
+
else
|
35
|
+
build_file_list_from_folder(step_definitions_location, ".rb").each { |location|
|
36
|
+
@step_definitions << build_step_definitions(location)
|
37
|
+
print '.'
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@step_definitions.flatten!
|
43
|
+
@summary = {
|
44
|
+
:total_score => 0,
|
45
|
+
:features => {},
|
46
|
+
:step_definitions => {},
|
47
|
+
:improvement_list => {}
|
48
|
+
}
|
49
|
+
puts "\nCataloging Step Calls: "
|
50
|
+
catalog_step_calls
|
51
|
+
puts "\nAssessing Score: "
|
52
|
+
assess_score
|
53
|
+
end
|
54
|
+
|
55
|
+
def good?
|
56
|
+
@summary[:total_score] <= Constants::THRESHOLDS["Project"]
|
57
|
+
end
|
58
|
+
|
59
|
+
def problem_percentage
|
60
|
+
@summary[:total_score].to_f / Constants::THRESHOLDS["Project"].to_f
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_file_list_from_folder(folder_name, extension)
|
64
|
+
list = []
|
65
|
+
Dir.entries(folder_name).each_entry do |file_name|
|
66
|
+
unless FILE_IGNORE_LIST.include?(file_name)
|
67
|
+
file_name = "#{folder_name}/#{file_name}"
|
68
|
+
if File.directory?(file_name)
|
69
|
+
list << build_file_list_from_folder(file_name, extension)
|
70
|
+
elsif file_name.downcase.include?(extension)
|
71
|
+
list << file_name
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
list.flatten
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_step_definitions(file_name)
|
79
|
+
step_file_lines = []
|
80
|
+
step_file = File.open(file_name)
|
81
|
+
step_file.each_line { |line| step_file_lines << line }
|
82
|
+
step_file.close
|
83
|
+
|
84
|
+
counter = 0
|
85
|
+
step_code = []
|
86
|
+
step_definitions = []
|
87
|
+
found_first_step = false
|
88
|
+
until counter >= step_file_lines.length
|
89
|
+
if step_file_lines[counter] =~ STEP_DEFINITION_REGEX and !step_code.empty? and found_first_step
|
90
|
+
step_definitions << CukeSniffer::StepDefinition.new("#{file_name}:#{counter+1 - step_code.count}", step_code)
|
91
|
+
step_code = []
|
92
|
+
end
|
93
|
+
found_first_step = true if step_file_lines[counter] =~ STEP_DEFINITION_REGEX
|
94
|
+
step_code << step_file_lines[counter].strip
|
95
|
+
counter+=1
|
96
|
+
end
|
97
|
+
step_definitions << CukeSniffer::StepDefinition.new("#{file_name}:#{counter+1}", step_code) unless step_code.empty? or !found_first_step
|
98
|
+
step_definitions
|
99
|
+
end
|
100
|
+
|
101
|
+
def assess_array(array, type)
|
102
|
+
min, max, min_file, max_file = nil
|
103
|
+
total = 0
|
104
|
+
good = 0
|
105
|
+
bad = 0
|
106
|
+
total_score = 0
|
107
|
+
array.each do |node|
|
108
|
+
score = node.score
|
109
|
+
@summary[:total_score] += score
|
110
|
+
total_score += score
|
111
|
+
node.rules_hash.each_key do |key|
|
112
|
+
@summary[:improvement_list][key] ||= 0
|
113
|
+
@summary[:improvement_list][key] += node.rules_hash[key]
|
114
|
+
end
|
115
|
+
min, min_file = score, node.location if (min.nil? or score < min)
|
116
|
+
max, max_file = score, node.location if (max.nil? or score > max)
|
117
|
+
if node.good?
|
118
|
+
good += 1
|
119
|
+
else
|
120
|
+
bad += 1
|
121
|
+
end
|
122
|
+
total += score
|
123
|
+
end
|
124
|
+
{
|
125
|
+
:total => array.count,
|
126
|
+
:total_score => total_score,
|
127
|
+
:min => min,
|
128
|
+
:min_file => min_file,
|
129
|
+
:max => max,
|
130
|
+
:max_file => max_file,
|
131
|
+
:average => (total.to_f/array.count.to_f).round(2),
|
132
|
+
:threshold => THRESHOLDS[type],
|
133
|
+
:good => good,
|
134
|
+
:bad => bad,
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def assess_score
|
139
|
+
@summary[:features] = assess_array(@features, "Feature")
|
140
|
+
@summary[:scenarios] = assess_array(@scenarios, "Scenario")
|
141
|
+
@summary[:step_definitions] = assess_array(@step_definitions, "StepDefinition") unless @step_definitions.empty?
|
142
|
+
sort_improvement_list
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_all_scenarios(features)
|
146
|
+
scenarios = []
|
147
|
+
features.each do |feature|
|
148
|
+
scenarios << feature.background unless feature.background.nil?
|
149
|
+
scenarios << feature.scenarios
|
150
|
+
end
|
151
|
+
scenarios.flatten
|
152
|
+
end
|
153
|
+
|
154
|
+
def sort_improvement_list
|
155
|
+
sorted_array = @summary[:improvement_list].sort_by { |improvement, occurrence| occurrence }
|
156
|
+
@summary[:improvement_list] = {}
|
157
|
+
sorted_array.reverse.each { |node|
|
158
|
+
@summary[:improvement_list][node[0]] = node[1]
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
def output_results
|
163
|
+
feature_results = @summary[:features]
|
164
|
+
step_definition_results = @summary[:step_definitions]
|
165
|
+
output = "Suite Summary
|
166
|
+
Total Score: #{@summary[:total_score]}
|
167
|
+
Features (#@features_location)
|
168
|
+
Min: #{feature_results[:min]} (#{feature_results[:min_file]})
|
169
|
+
Max: #{feature_results[:max]} (#{feature_results[:max_file]})
|
170
|
+
Average: #{feature_results[:average]}
|
171
|
+
Step Definitions (#@step_definitions_location)
|
172
|
+
Min: #{step_definition_results[:min]} (#{step_definition_results[:min_file]})
|
173
|
+
Max: #{step_definition_results[:max]} (#{step_definition_results[:max_file]})
|
174
|
+
Average: #{step_definition_results[:average]}
|
175
|
+
Improvements to make:"
|
176
|
+
create_improvement_list.each { |item| output << "\n #{item}" }
|
177
|
+
output
|
178
|
+
end
|
179
|
+
|
180
|
+
def create_improvement_list
|
181
|
+
output = []
|
182
|
+
@summary[:improvement_list].each_key { |improvement| output << "(#{summary[:improvement_list][improvement]})#{improvement}" }
|
183
|
+
output
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_all_steps
|
187
|
+
steps = {}
|
188
|
+
@features.each do |feature|
|
189
|
+
feature.scenarios.each do |scenario|
|
190
|
+
counter = 1
|
191
|
+
scenario.steps.each do |step|
|
192
|
+
location = scenario.location.gsub(/:\d*$/, ":#{scenario.start_line + counter}")
|
193
|
+
steps[location] = step
|
194
|
+
counter += 1
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
@step_definitions.each do |definition|
|
199
|
+
definition.nested_steps.each_key do |key|
|
200
|
+
steps[key] = definition.nested_steps[key]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
steps
|
204
|
+
end
|
205
|
+
|
206
|
+
def catalog_step_calls
|
207
|
+
steps = get_all_steps
|
208
|
+
@step_definitions.each do |step_definition|
|
209
|
+
print '.'
|
210
|
+
calls = steps.find_all { |location, step| step.gsub(STEP_STYLES, "") =~ step_definition.regex }
|
211
|
+
calls.each { |call|
|
212
|
+
step_definition.add_call(call[0], call[1].gsub(STEP_STYLES, ""))
|
213
|
+
}
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def get_dead_steps
|
218
|
+
dead_steps = []
|
219
|
+
@step_definitions.each do |step_definition|
|
220
|
+
dead_steps << step_definition if step_definition.calls.empty?
|
221
|
+
end
|
222
|
+
|
223
|
+
dead_steps.sort_by{|step| step.location}
|
224
|
+
end
|
225
|
+
|
226
|
+
def extract_markup
|
227
|
+
markup_location = File.join(File.dirname(__FILE__), 'report', 'markup.rhtml')
|
228
|
+
markup = ""
|
229
|
+
File.open(markup_location).lines.each do |line|
|
230
|
+
markup << line
|
231
|
+
end
|
232
|
+
markup
|
233
|
+
end
|
234
|
+
|
235
|
+
def output_html(file_name = "cuke_sniffer_results.html", cuke_sniffer = self)
|
236
|
+
@features = @features.sort_by { |feature| feature.total_score }.reverse
|
237
|
+
@step_definitions = @step_definitions.sort_by { |step_definition| step_definition.score }.reverse
|
238
|
+
|
239
|
+
markup_erb = ERB.new extract_markup
|
240
|
+
output = markup_erb.result(binding)
|
241
|
+
File.open(file_name, 'w') do |f|
|
242
|
+
f.write(output)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|