cuke_slicer 1.0.0 → 2.0.0

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.
@@ -0,0 +1,32 @@
1
+ require "cuke_slicer/helpers/matching_helpers"
2
+ require "cuke_slicer/helpers/filter_helpers"
3
+ require "cuke_slicer/helpers/extraction_helpers"
4
+
5
+
6
+ module CukeSlicer
7
+ class FileExtractor
8
+
9
+ include ExtractionHelpers
10
+
11
+
12
+ def extract(target, filters, format, &block)
13
+ Array.new.tap do |test_cases|
14
+ unless target.feature.nil?
15
+ tests = target.feature.tests
16
+
17
+ runnable_elements = extract_runnable_elements(extract_runnable_block_elements(tests, filters, &block))
18
+
19
+ runnable_elements.each do |element|
20
+ case
21
+ when format == :file_line
22
+ test_cases << "#{element.get_ancestor(:feature_file).path}:#{element.source_line}"
23
+ when format == :test_object
24
+ test_cases << element
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,42 @@
1
+ require "cuke_slicer/helpers/helpers"
2
+ require "cuke_slicer/collections/tag_collection"
3
+ require "cuke_slicer/collections/path_collection"
4
+
5
+
6
+ module CukeSlicer
7
+ class FilterSet
8
+
9
+ include Helpers
10
+
11
+
12
+ def initialize filter_type, filter_value
13
+ self.filter_type = filter_type
14
+ self.filter_value = filter_value
15
+ end
16
+
17
+ def validate
18
+ block_unknown
19
+ block_invalid
20
+
21
+ if filter_value.is_a?(Array)
22
+ TagCollection.new(filter_value).validate if is_tag?(filter_type)
23
+ PathCollection.new(filter_value).validate if is_path?(filter_type)
24
+ end
25
+ end
26
+
27
+
28
+ private
29
+
30
+
31
+ def block_unknown
32
+ raise(ArgumentError, "Unknown filter '#{filter_type}'") unless CukeSlicer::Slicer.known_filters.include?(filter_type)
33
+ end
34
+
35
+ def block_invalid
36
+ raise(ArgumentError, "Invalid filter '#{filter_value}'. Must be a String, Regexp, or Array thereof. Got #{filter_value.class}") unless str_regex_arr?(filter_value)
37
+ end
38
+
39
+ attr_accessor :filter_type, :filter_value
40
+
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ module CukeSlicer
2
+ module ExtractionHelpers
3
+
4
+ include FilterHelpers
5
+
6
+
7
+ def extract_runnable_block_elements(things, filters, &block)
8
+ Array.new.tap do |elements|
9
+ things.each do |thing|
10
+ if thing.is_a?(CukeModeler::Outline)
11
+ elements.concat(thing.examples)
12
+ else
13
+ elements << thing
14
+ end
15
+ end
16
+
17
+ filter_excluded_paths(elements, filters[:excluded_paths])
18
+ filter_included_paths(elements, filters[:included_paths])
19
+ filter_excluded_tags(elements, filters[:excluded_tags])
20
+ filter_included_tags(elements, filters[:included_tags])
21
+
22
+ apply_custom_filter(elements, &block)
23
+ end
24
+ end
25
+
26
+ def extract_runnable_elements(things)
27
+ Array.new.tap do |elements|
28
+ things.each do |thing|
29
+ if thing.is_a?(CukeModeler::Example)
30
+ # Slicing in order to remove the parameter row element
31
+ elements.concat(thing.row_elements.slice(1, thing.row_elements.count - 1))
32
+ else
33
+ elements << thing
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ module CukeSlicer
2
+ module FilterHelpers
3
+
4
+ include MatchingHelpers
5
+
6
+
7
+ def apply_custom_filter(elements, &block)
8
+ if block
9
+ elements.reject! do |element|
10
+ block.call(element)
11
+ end
12
+ end
13
+ end
14
+
15
+ def filter_excluded_tags(elements, filters)
16
+ if filters
17
+ filters = [filters] unless filters.is_a?(Array)
18
+
19
+ unless filters.empty?
20
+ elements.reject! do |element|
21
+ matching_tag?(element, filters)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ def filter_included_tags(elements, filters)
28
+ if filters
29
+ filters = [filters] unless filters.is_a?(Array)
30
+
31
+ elements.keep_if do |element|
32
+ matching_tag?(element, filters)
33
+ end
34
+ end
35
+ end
36
+
37
+ def filter_excluded_paths(elements, filters)
38
+ if filters
39
+ filters = [filters] unless filters.is_a?(Array)
40
+
41
+ elements.reject! do |element|
42
+ matching_path?(element, filters)
43
+ end
44
+ end
45
+ end
46
+
47
+ def filter_included_paths(elements, filters)
48
+ if filters
49
+ filters = [filters] unless filters.is_a?(Array)
50
+
51
+ unless filters.empty?
52
+ elements.keep_if do |element|
53
+ matching_path?(element, filters)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ module CukeSlicer
2
+ module Helpers
3
+
4
+ def str_regex?(parameter)
5
+ parameter.is_a?(String) or parameter.is_a?(Regexp)
6
+ end
7
+
8
+ def str_regex_arr?(parameter)
9
+ parameter.is_a?(String) or parameter.is_a?(Regexp) or parameter.is_a?(Array)
10
+ end
11
+
12
+ def is_tag?(parameter)
13
+ parameter.to_s =~ /tag/
14
+ end
15
+
16
+ def is_path?(parameter)
17
+ parameter.to_s =~ /path/
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,47 @@
1
+ module CukeSlicer
2
+ module MatchingHelpers
3
+
4
+ def matching_tag?(element, filters)
5
+ filters.each do |filter|
6
+ if filter.is_a?(Array)
7
+ filter_match = or_filter_match(element, filter)
8
+ else
9
+ filter_match = and_filter_match(element, filter)
10
+ end
11
+
12
+ return false unless filter_match
13
+ end
14
+
15
+ true
16
+ end
17
+
18
+ def and_filter_match(element, filter)
19
+ filter_match(element, filter)
20
+ end
21
+
22
+ def or_filter_match(element, filters)
23
+ filters.any? do |filter|
24
+ filter_match(element, filter)
25
+ end
26
+ end
27
+
28
+ def filter_match(element, filter)
29
+ if filter.is_a?(Regexp)
30
+ element.all_tags.any? { |tag| tag =~ filter }
31
+ else
32
+ element.all_tags.include?(filter)
33
+ end
34
+ end
35
+
36
+ def matching_path?(element, filters)
37
+ filters.any? do |filtered_path|
38
+ if filtered_path.is_a?(Regexp)
39
+ element.get_ancestor(:feature_file).path =~ filtered_path
40
+ else
41
+ element.get_ancestor(:feature_file).path == filtered_path
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -1,3 +1,8 @@
1
+ require "cuke_slicer/extractors/directory_extractor"
2
+ require "cuke_slicer/extractors/file_extractor"
3
+ require "cuke_slicer/filters/filter_set"
4
+
5
+
1
6
  module CukeSlicer
2
7
 
3
8
  # The object responsible for slicing up a Cucumber test suite into discrete test cases.
@@ -13,12 +18,16 @@ module CukeSlicer
13
18
  # exposes the underlying modeling objects and knowledge of how they work is then required to make good use of the
14
19
  # filter.
15
20
  #
21
+ # Finally, the test cases can be provided as a collection of file:line strings or as a collection of the object
22
+ # types used to represent test cases by the underlying modeling library.
23
+ #
16
24
  # @param target [String] the location that will be sliced up
17
25
  # @param filters [Hash] the filters that will be applied to the sliced test cases
18
- def slice(target, filters = {}, &block)
26
+ # @param format [Symbol] the type of output: :file_line or :test_object
27
+ def slice(target, filters = {}, format, &block)
19
28
  validate_target(target)
20
29
  validate_filters(filters)
21
-
30
+ validate_format(format)
22
31
 
23
32
  begin
24
33
  target = File.directory?(target) ? CukeModeler::Directory.new(target) : CukeModeler::FeatureFile.new(target)
@@ -27,9 +36,9 @@ module CukeSlicer
27
36
  end
28
37
 
29
38
  if target.is_a?(CukeModeler::Directory)
30
- sliced_tests = extract_test_cases_from_directory(target, filters, &block)
39
+ sliced_tests = DirectoryExtractor.new.extract(target, filters, format, &block)
31
40
  else
32
- sliced_tests = extract_test_cases_from_file(target, filters, &block)
41
+ sliced_tests = FileExtractor.new.extract(target, filters, format, &block)
33
42
  end
34
43
 
35
44
  sliced_tests
@@ -51,198 +60,13 @@ module CukeSlicer
51
60
  raise(ArgumentError, "File or directory '#{target}' does not exist") unless File.exists?(target.to_s)
52
61
  end
53
62
 
54
- def validate_filters(filter_sets)
55
- filter_sets.each do |filter_type, filter_value|
56
- raise(ArgumentError, "Unknown filter '#{filter_type}'") unless self.class.known_filters.include?(filter_type)
57
- raise(ArgumentError, "Invalid filter '#{filter_value}'. Must be a String, Regexp, or Array thereof. Got #{filter_value.class}") unless filter_value.is_a?(String) or filter_value.is_a?(Regexp) or filter_value.is_a?(Array)
58
-
59
- if filter_value.is_a?(Array)
60
- validate_tag_collection(filter_value) if filter_type.to_s =~ /tag/
61
- validate_path_collection(filter_value) if filter_type.to_s =~ /path/
62
- end
63
- end
64
- end
65
-
66
- def validate_tag_collection(filter_collection)
67
- filter_collection.each do |filter|
68
- raise(ArgumentError, "Filter '#{filter}' must be a String, Regexp, or Array. Got #{filter.class}") unless filter.is_a?(String) or filter.is_a?(Regexp) or filter.is_a?(Array)
69
-
70
- validate_nested_tag_collection(filter) if filter.is_a?(Array)
71
- end
72
- end
73
-
74
- def validate_nested_tag_collection(filter_collection)
75
- filter_collection.each do |filter|
76
- raise(ArgumentError, "Tag filters cannot be nested more than one level deep.") if filter.is_a?(Array)
77
- raise(ArgumentError, "Filter '#{filter}' must be a String or Regexp. Got #{filter.class}") unless filter.is_a?(String) or filter.is_a?(Regexp)
78
- end
79
- end
80
-
81
- def validate_path_collection(filter_collection)
82
- filter_collection.each do |filter|
83
- raise(ArgumentError, "Filter '#{filter}' must be a String or Regexp. Got #{filter.class}") unless filter.is_a?(String) or filter.is_a?(Regexp)
84
- end
85
- end
86
-
87
- def extract_test_cases_from_directory(target, filters, &block)
88
- entries = Dir.entries(target.path)
89
- entries.delete '.'
90
- entries.delete '..'
91
-
92
- Array.new.tap do |test_cases|
93
- entries.each do |entry|
94
- entry = "#{target.path}/#{entry}"
95
-
96
- case
97
- when File.directory?(entry)
98
- test_cases.concat(extract_test_cases_from_directory(CukeModeler::Directory.new(entry), filters, &block))
99
- when entry =~ /\.feature$/
100
- test_cases.concat(extract_test_cases_from_file(CukeModeler::FeatureFile.new(entry), filters, &block))
101
- else
102
- # Non-feature files are ignored
103
- end
104
- end
105
- end
106
- end
107
-
108
- def extract_test_cases_from_file(target, filters, &block)
109
- Array.new.tap do |test_cases|
110
- unless target.feature.nil?
111
- tests = target.feature.tests
112
-
113
- runnable_elements = extract_runnable_elements(extract_runnable_block_elements(tests, filters))
114
-
115
- apply_custom_filter(runnable_elements, &block)
116
-
117
- runnable_elements.each do |element|
118
- test_cases << "#{element.get_ancestor(:feature_file).path}:#{element.source_line}"
119
- end
120
- end
121
- end
122
- end
123
-
124
- def extract_runnable_block_elements(things, filters)
125
- Array.new.tap do |elements|
126
- things.each do |thing|
127
- if thing.is_a?(CukeModeler::Outline)
128
- elements.concat(thing.examples)
129
- else
130
- elements << thing
131
- end
132
- end
133
-
134
- filter_excluded_paths(elements, filters[:excluded_paths])
135
- filter_included_paths(elements, filters[:included_paths])
136
- filter_excluded_tags(elements, filters[:excluded_tags])
137
- filter_included_tags(elements, filters[:included_tags])
138
- end
139
- end
140
-
141
- def extract_runnable_elements(things)
142
- Array.new.tap do |elements|
143
- things.each do |thing|
144
- if thing.is_a?(CukeModeler::Example)
145
- # Slicing in order to remove the parameter row element
146
- elements.concat(thing.row_elements.slice(1, thing.row_elements.count - 1))
147
- else
148
- elements << thing
149
- end
150
- end
151
- end
152
- end
153
-
154
- def apply_custom_filter(elements, &block)
155
- if block
156
- elements.reject! do |element|
157
- block.call(element)
158
- end
159
- end
160
- end
161
-
162
- def filter_excluded_tags(elements, filters)
163
- if filters
164
- filters = [filters] unless filters.is_a?(Array)
165
-
166
- unless filters.empty?
167
- elements.reject! do |element|
168
- matching_tag?(element, filters)
169
- end
170
- end
171
- end
172
- end
173
-
174
- def filter_included_tags(elements, filters)
175
- if filters
176
- filters = [filters] unless filters.is_a?(Array)
177
-
178
- elements.keep_if do |element|
179
- matching_tag?(element, filters)
180
- end
181
- end
182
- end
183
-
184
- def filter_excluded_paths(elements, filters)
185
- if filters
186
- filters = [filters] unless filters.is_a?(Array)
187
-
188
- elements.reject! do |element|
189
- matching_path?(element, filters)
190
- end
191
- end
192
- end
193
-
194
- def filter_included_paths(elements, filters)
195
- if filters
196
- filters = [filters] unless filters.is_a?(Array)
197
-
198
- unless filters.empty?
199
- elements.keep_if do |element|
200
- matching_path?(element, filters)
201
- end
202
- end
203
- end
204
- end
205
-
206
- def matching_tag?(element, filters)
207
- filters.each do |filter|
208
- if filter.is_a?(Array)
209
- filter_match = or_filter_match(element, filter)
210
- else
211
- filter_match = and_filter_match(element, filter)
212
- end
213
-
214
- return false unless filter_match
215
- end
216
-
217
- true
218
- end
219
-
220
- def and_filter_match(element, filter)
221
- filter_match(element, filter)
63
+ def validate_filters(filters)
64
+ filter_sets = filters.map { |filter_type, value| FilterSet.new(filter_type, value) }
65
+ filter_sets.each(&:validate)
222
66
  end
223
67
 
224
- def or_filter_match(element, filters)
225
- filters.any? do |filter|
226
- filter_match(element, filter)
227
- end
228
- end
229
-
230
- def filter_match(element, filter)
231
- if filter.is_a?(Regexp)
232
- element.all_tags.any? { |tag| tag =~ filter }
233
- else
234
- element.all_tags.include?(filter)
235
- end
236
- end
237
-
238
- def matching_path?(element, filters)
239
- filters.any? do |filtered_path|
240
- if filtered_path.is_a?(Regexp)
241
- element.get_ancestor(:feature_file).path =~ filtered_path
242
- else
243
- element.get_ancestor(:feature_file).path == filtered_path
244
- end
245
- end
68
+ def validate_format(format)
69
+ raise(ArgumentError, "Invalid Output Format: #{format}") unless [:test_object, :file_line].include?(format)
246
70
  end
247
71
 
248
72
  end