cuke_slicer 1.0.0 → 2.2.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +33 -0
- data/CHANGELOG.md +67 -0
- data/LICENSE.txt +2 -1
- data/README.md +18 -1
- data/Rakefile +85 -49
- data/appveyor.yml +81 -0
- data/cuke_slicer.gemspec +13 -7
- data/features/step_definitions/action_steps.rb +12 -11
- data/features/step_definitions/setup_steps.rb +24 -0
- data/features/step_definitions/verification_steps.rb +11 -0
- data/features/test_case_extraction.feature +7 -1
- data/features/validation.feature +6 -0
- data/gemfiles/cuke_modeler0.gemfile +28 -0
- data/gemfiles/cuke_modeler1.gemfile +29 -0
- data/gemfiles/cuke_modeler2.gemfile +29 -0
- data/gemfiles/cuke_modeler3.gemfile +12 -0
- data/lib/cuke_slicer.rb +10 -10
- data/lib/cuke_slicer/collections/nested_tag_collection.rb +29 -0
- data/lib/cuke_slicer/collections/path_collection.rb +28 -0
- data/lib/cuke_slicer/collections/tag_collection.rb +30 -0
- data/lib/cuke_slicer/extractors/directory_extractor.rb +19 -0
- data/lib/cuke_slicer/extractors/file_extractor.rb +34 -0
- data/lib/cuke_slicer/filters/filter_set.rb +44 -0
- data/lib/cuke_slicer/helpers/extraction_helpers.rb +44 -0
- data/lib/cuke_slicer/helpers/filter_helpers.rb +62 -0
- data/lib/cuke_slicer/helpers/helpers.rb +23 -0
- data/lib/cuke_slicer/helpers/matching_helpers.rb +52 -0
- data/lib/cuke_slicer/slicer.rb +25 -196
- data/lib/cuke_slicer/version.rb +1 -1
- data/spec/cuke_slicer_spec.rb +15 -0
- data/spec/slicer_integration_spec.rb +151 -43
- data/spec/slicer_unit_spec.rb +3 -3
- metadata +98 -54
@@ -0,0 +1,23 @@
|
|
1
|
+
# Internal helper module that is not part of the public API. Subject to change at any time.
|
2
|
+
# :nodoc: all
|
3
|
+
module CukeSlicer
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
def str_regex?(parameter)
|
7
|
+
parameter.is_a?(String) or parameter.is_a?(Regexp)
|
8
|
+
end
|
9
|
+
|
10
|
+
def str_regex_arr?(parameter)
|
11
|
+
parameter.is_a?(String) or parameter.is_a?(Regexp) or parameter.is_a?(Array)
|
12
|
+
end
|
13
|
+
|
14
|
+
def is_tag?(parameter)
|
15
|
+
parameter.to_s =~ /tag/
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_path?(parameter)
|
19
|
+
parameter.to_s =~ /path/
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Internal helper module that is not part of the public API. Subject to change at any time.
|
2
|
+
# :nodoc: all
|
3
|
+
module CukeSlicer
|
4
|
+
module MatchingHelpers
|
5
|
+
|
6
|
+
def matching_tag?(element, filters)
|
7
|
+
filters.each do |filter|
|
8
|
+
if filter.is_a?(Array)
|
9
|
+
filter_match = or_filter_match(element, filter)
|
10
|
+
else
|
11
|
+
filter_match = and_filter_match(element, filter)
|
12
|
+
end
|
13
|
+
|
14
|
+
return false unless filter_match
|
15
|
+
end
|
16
|
+
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def and_filter_match(element, filter)
|
21
|
+
filter_match(element, filter)
|
22
|
+
end
|
23
|
+
|
24
|
+
def or_filter_match(element, filters)
|
25
|
+
filters.any? do |filter|
|
26
|
+
filter_match(element, filter)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def filter_match(element, filter)
|
31
|
+
tag_values = element.all_tags
|
32
|
+
tag_values = tag_values.collect { |tag| tag.name } unless Gem.loaded_specs['cuke_modeler'].version.version[/^[0]/]
|
33
|
+
|
34
|
+
if filter.is_a?(Regexp)
|
35
|
+
tag_values.any? { |tag| tag =~ filter }
|
36
|
+
else
|
37
|
+
tag_values.include?(filter)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def matching_path?(element, filters)
|
42
|
+
filters.any? do |filtered_path|
|
43
|
+
if filtered_path.is_a?(Regexp)
|
44
|
+
element.get_ancestor(:feature_file).path =~ filtered_path
|
45
|
+
else
|
46
|
+
element.get_ancestor(:feature_file).path == filtered_path
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/lib/cuke_slicer/slicer.rb
CHANGED
@@ -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,23 +18,32 @@ 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
|
-
|
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)
|
25
|
-
rescue
|
26
|
-
|
34
|
+
rescue => e
|
35
|
+
if e.message =~ /lexing|parsing/i
|
36
|
+
raise(ArgumentError, "A syntax or lexing problem was encountered while trying to parse #{target}")
|
37
|
+
else
|
38
|
+
raise e
|
39
|
+
end
|
40
|
+
|
27
41
|
end
|
28
42
|
|
29
43
|
if target.is_a?(CukeModeler::Directory)
|
30
|
-
sliced_tests =
|
44
|
+
sliced_tests = DirectoryExtractor.new.extract(target, filters, format, &block)
|
31
45
|
else
|
32
|
-
sliced_tests =
|
46
|
+
sliced_tests = FileExtractor.new.extract(target, filters, format, &block)
|
33
47
|
end
|
34
48
|
|
35
49
|
sliced_tests
|
@@ -51,198 +65,13 @@ module CukeSlicer
|
|
51
65
|
raise(ArgumentError, "File or directory '#{target}' does not exist") unless File.exists?(target.to_s)
|
52
66
|
end
|
53
67
|
|
54
|
-
def validate_filters(
|
55
|
-
filter_sets.
|
56
|
-
|
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
|
68
|
+
def validate_filters(filters)
|
69
|
+
filter_sets = filters.map { |filter_type, value| FilterSet.new(filter_type, value) }
|
70
|
+
filter_sets.each(&:validate)
|
106
71
|
end
|
107
72
|
|
108
|
-
def
|
109
|
-
|
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)
|
222
|
-
end
|
223
|
-
|
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
|
73
|
+
def validate_format(format)
|
74
|
+
raise(ArgumentError, "Invalid Output Format: #{format}") unless [:test_object, :file_line].include?(format)
|
246
75
|
end
|
247
76
|
|
248
77
|
end
|
data/lib/cuke_slicer/version.rb
CHANGED
data/spec/cuke_slicer_spec.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require "#{File.dirname(__FILE__)}/spec_helper"
|
3
|
+
require 'rubygems/mock_gem_ui'
|
2
4
|
|
3
5
|
|
4
6
|
describe 'CukeSlicer, Unit' do
|
@@ -10,4 +12,17 @@ describe 'CukeSlicer, Unit' do
|
|
10
12
|
expect(Kernel.const_defined?(:CukeSlicer)).to be true
|
11
13
|
end
|
12
14
|
|
15
|
+
describe 'the gem' do
|
16
|
+
|
17
|
+
let(:gemspec) { eval(File.read "#{File.dirname(__FILE__)}/../cuke_slicer.gemspec") }
|
18
|
+
|
19
|
+
it 'validates cleanly' do
|
20
|
+
mock_ui = Gem::MockGemUi.new
|
21
|
+
Gem::DefaultUserInteraction.use_ui(mock_ui) { gemspec.validate }
|
22
|
+
|
23
|
+
expect(mock_ui.error).to_not match(/warn/i)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
13
28
|
end
|
@@ -3,74 +3,130 @@ require 'spec_helper'
|
|
3
3
|
|
4
4
|
describe 'Slicer, Integration' do
|
5
5
|
|
6
|
-
let
|
6
|
+
let(:clazz) { CukeSlicer::Slicer }
|
7
7
|
let(:slicer) { clazz.new }
|
8
8
|
let(:test_file) { "#{@default_file_directory}/a_test.feature" }
|
9
|
+
let(:test_file_text) { "Feature: Test feature
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@tag
|
14
|
-
Scenario: Test scenario
|
15
|
-
* some step"
|
11
|
+
@tag
|
12
|
+
Scenario: Test scenario
|
13
|
+
* some step" }
|
16
14
|
|
17
|
-
|
15
|
+
before(:each) do
|
16
|
+
File.write(test_file, test_file_text)
|
18
17
|
end
|
19
18
|
|
20
19
|
|
21
20
|
describe 'slicing' do
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
describe 'output' do
|
23
|
+
|
24
|
+
it 'slicing returns a collection of test source lines' do
|
25
|
+
slice_output = slicer.slice(test_file, :file_line)
|
26
|
+
|
27
|
+
expect(slice_output).to be_an(Array)
|
28
|
+
expect(slice_output).to_not be_empty
|
29
|
+
|
30
|
+
slice_output.each do |test_case|
|
31
|
+
# Test cases come in 'file_path:line_number' format
|
32
|
+
expect(test_case).to match(/^.+:\d+$/)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'slicing returns a collection of test objects' do
|
37
|
+
slice_output = slicer.slice(test_file, :test_object)
|
38
|
+
|
39
|
+
expect(slice_output).to be_an(Array)
|
40
|
+
expect(slice_output).to_not be_empty
|
25
41
|
|
26
|
-
|
27
|
-
|
42
|
+
slice_output.each do |test_case|
|
43
|
+
# Test cases come as test objects
|
44
|
+
expect(test_case).to be_a(CukeModeler::Scenario).or be_a(CukeModeler::Row)
|
45
|
+
end
|
46
|
+
end
|
28
47
|
|
29
|
-
|
30
|
-
|
31
|
-
expect(test_case).to match(/^.+:\d+$/)
|
48
|
+
it 'complains if told to provide output in an unknown format' do
|
49
|
+
expect { slicer.slice(test_file, :bad_option) }.to raise_error(ArgumentError, /Invalid Output Format/)
|
32
50
|
end
|
51
|
+
|
33
52
|
end
|
34
53
|
|
35
54
|
it 'can slice without being provided filters' do
|
36
|
-
expect { slicer.slice(test_file) }.to_not raise_error
|
55
|
+
expect { slicer.slice(test_file, :file_line) }.to_not raise_error
|
37
56
|
end
|
38
57
|
|
39
58
|
it 'uses the custom filter, if provided' do
|
40
|
-
expect { |test_block| slicer.slice(@default_file_directory, &test_block) }.to yield_control
|
41
|
-
expect { slicer.slice(@default_file_directory) }.to_not raise_error
|
59
|
+
expect { |test_block| slicer.slice(@default_file_directory, :file_line, &test_block) }.to yield_control
|
60
|
+
expect { slicer.slice(@default_file_directory, :file_line) }.to_not raise_error
|
42
61
|
end
|
43
62
|
|
44
63
|
it 'can slice an empty feature file' do
|
45
64
|
File.open(test_file, 'w') { |file| file.write('') }
|
46
65
|
|
47
|
-
expect { slicer.slice(test_file) }.to_not raise_error
|
66
|
+
expect { slicer.slice(test_file, :file_line) }.to_not raise_error
|
48
67
|
end
|
49
68
|
|
50
69
|
it 'can slice a feature that has no tests' do
|
51
70
|
File.open(test_file, 'w') { |file| file.write('Feature: Empty feature') }
|
52
71
|
|
53
|
-
expect { slicer.slice(test_file) }.to_not raise_error
|
72
|
+
expect { slicer.slice(test_file, :file_line) }.to_not raise_error
|
54
73
|
end
|
55
74
|
|
56
75
|
it 'can slice a directory that contains non-feature files' do
|
57
76
|
File.open("#{@default_file_directory}/not_a_feature.file", 'w') { |file| file.write('foobar') }
|
58
77
|
|
59
|
-
expect { slicer.slice(@default_file_directory) }.to_not raise_error
|
78
|
+
expect { slicer.slice(@default_file_directory, :file_line) }.to_not raise_error
|
60
79
|
end
|
61
80
|
|
62
81
|
|
63
82
|
describe 'target validation' do
|
64
83
|
|
65
84
|
it 'complains if told to slice a non-existent location' do
|
66
|
-
expect { slicer.slice('does/not/exist') }.to raise_error(ArgumentError, /does not exist/)
|
67
|
-
expect { slicer.slice(nil) }.to raise_error(ArgumentError, /does not exist/)
|
85
|
+
expect { slicer.slice('does/not/exist', :file_line) }.to raise_error(ArgumentError, /does not exist/)
|
86
|
+
expect { slicer.slice(nil, :file_line) }.to raise_error(ArgumentError, /does not exist/)
|
68
87
|
end
|
69
88
|
|
70
89
|
it 'complains if told to slice an incorrectly formatted feature file' do
|
71
90
|
File.open(test_file, 'w') { |file| file.write('foobar') }
|
72
91
|
|
73
|
-
expect { slicer.slice(test_file) }.to raise_error(ArgumentError, /syntax.*lexing problem.*#{test_file}/i)
|
92
|
+
expect { slicer.slice(test_file, :file_line) }.to raise_error(ArgumentError, /syntax.*lexing problem.*#{test_file}/i)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'does not swallow unexpected exceptions while slicing' do
|
96
|
+
begin
|
97
|
+
$old_method = CukeModeler::Parsing.method(:parse_text)
|
98
|
+
|
99
|
+
# Custom error type in order to ensure that we are throwing the correct thing
|
100
|
+
module CukeSlicer
|
101
|
+
class TestError < StandardError
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Monkey patch the parsing method to throw the error that we need for testing
|
106
|
+
module CukeModeler
|
107
|
+
module Parsing
|
108
|
+
class << self
|
109
|
+
def parse_text(*args)
|
110
|
+
raise(CukeSlicer::TestError, 'something went wrong')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
File.write(test_file, 'junk text')
|
118
|
+
|
119
|
+
expect { slicer.slice(test_file, :file_line) }.to raise_error(CukeSlicer::TestError, 'something went wrong')
|
120
|
+
ensure
|
121
|
+
# Making sure that our changes don't escape a test and ruin the rest of the suite
|
122
|
+
module CukeModeler
|
123
|
+
module Parsing
|
124
|
+
class << self
|
125
|
+
define_method(:parse_text, $old_method)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
74
130
|
end
|
75
131
|
|
76
132
|
end
|
@@ -84,13 +140,13 @@ describe 'Slicer, Integration' do
|
|
84
140
|
filters = clazz.known_filters
|
85
141
|
|
86
142
|
filters.each do |filter|
|
87
|
-
not_provided = slicer.slice(test_file)
|
143
|
+
not_provided = slicer.slice(test_file, :file_line)
|
88
144
|
|
89
145
|
case
|
90
146
|
when filter.to_s =~ /path/
|
91
|
-
nothing_provided = slicer.slice(test_file, filter => [])
|
147
|
+
nothing_provided = slicer.slice(test_file, {filter => []}, :file_line)
|
92
148
|
when filter.to_s =~ /tag/
|
93
|
-
nothing_provided = slicer.slice(test_file, filter => [])
|
149
|
+
nothing_provided = slicer.slice(test_file, {filter => []}, :file_line)
|
94
150
|
else
|
95
151
|
raise(ArgumentError, "Unknown filter '#{filter}'")
|
96
152
|
end
|
@@ -114,7 +170,7 @@ describe 'Slicer, Integration' do
|
|
114
170
|
expect(applied_filters.keys).to match_array(filters)
|
115
171
|
|
116
172
|
|
117
|
-
expect { @slice_output = slicer.slice(@default_file_directory, applied_filters, &block_filter) }.to_not raise_error
|
173
|
+
expect { @slice_output = slicer.slice(@default_file_directory, applied_filters, :file_line, &block_filter) }.to_not raise_error
|
118
174
|
expect(@slice_output).to be_an(Array)
|
119
175
|
expect(@slice_output).to_not be_empty
|
120
176
|
end
|
@@ -126,11 +182,11 @@ describe 'Slicer, Integration' do
|
|
126
182
|
path_filter_types = clazz.known_filters.select { |filter| filter.to_s =~ /path/ }
|
127
183
|
|
128
184
|
path_filter_types.each do |filter|
|
129
|
-
expect { slicer.slice(@default_file_directory, filter => '@some_value') }.to_not raise_error
|
130
|
-
expect { slicer.slice(@default_file_directory, filter => /some_pattern/)
|
131
|
-
expect { slicer.slice(@default_file_directory, filter => ['@some_value', /some_pattern/])
|
132
|
-
expect { slicer.slice(@default_file_directory, filter => :something_else) }.to raise_error(ArgumentError, /must be a/i)
|
133
|
-
expect { slicer.slice(@default_file_directory, filter => [:something_else]) }.to raise_error(ArgumentError, /must be a/i)
|
185
|
+
expect { slicer.slice(@default_file_directory, {filter => '@some_value'}, :file_line) }.to_not raise_error
|
186
|
+
expect { slicer.slice(@default_file_directory, {filter => /some_pattern/}, :file_line)}.to_not raise_error
|
187
|
+
expect { slicer.slice(@default_file_directory, {filter => ['@some_value', /some_pattern/]}, :file_line)}.to_not raise_error
|
188
|
+
expect { slicer.slice(@default_file_directory, {filter => :something_else}, :file_line) }.to raise_error(ArgumentError, /must be a/i)
|
189
|
+
expect { slicer.slice(@default_file_directory, {filter => [:something_else]}, :file_line) }.to raise_error(ArgumentError, /must be a/i)
|
134
190
|
end
|
135
191
|
end
|
136
192
|
|
@@ -138,13 +194,13 @@ describe 'Slicer, Integration' do
|
|
138
194
|
tag_filter_types = clazz.known_filters.select { |filter| filter.to_s =~ /tag/ }
|
139
195
|
|
140
196
|
tag_filter_types.each do |filter|
|
141
|
-
expect { slicer.slice(@default_file_directory, filter => '@some_value') }.to_not raise_error
|
142
|
-
expect { slicer.slice(@default_file_directory, filter => /some_pattern/) }.to_not raise_error
|
143
|
-
expect { slicer.slice(@default_file_directory, filter => ['@some_value', /some_pattern/]) }.to_not raise_error
|
144
|
-
expect { slicer.slice(@default_file_directory, filter => ['@some_value', [/nested_pattern/]]) }.to_not raise_error
|
145
|
-
expect { slicer.slice(@default_file_directory, filter => ['@some_value', [/nested_pattern/, :bad_value]]) }.to raise_error(ArgumentError, /must be a/i)
|
146
|
-
expect { slicer.slice(@default_file_directory, filter => :something_else) }.to raise_error(ArgumentError, /must be a/i)
|
147
|
-
expect { slicer.slice(@default_file_directory, filter => [:something_else]) }.to raise_error(ArgumentError, /must be a/i)
|
197
|
+
expect { slicer.slice(@default_file_directory, {filter => '@some_value'}, :file_line) }.to_not raise_error
|
198
|
+
expect { slicer.slice(@default_file_directory, {filter => /some_pattern/}, :file_line) }.to_not raise_error
|
199
|
+
expect { slicer.slice(@default_file_directory, {filter => ['@some_value', /some_pattern/]}, :file_line) }.to_not raise_error
|
200
|
+
expect { slicer.slice(@default_file_directory, {filter => ['@some_value', [/nested_pattern/]]}, :file_line) }.to_not raise_error
|
201
|
+
expect { slicer.slice(@default_file_directory, {filter => ['@some_value', [/nested_pattern/, :bad_value]]}, :file_line) }.to raise_error(ArgumentError, /must be a/i)
|
202
|
+
expect { slicer.slice(@default_file_directory, {filter => :something_else}, :file_line) }.to raise_error(ArgumentError, /must be a/i)
|
203
|
+
expect { slicer.slice(@default_file_directory, {filter => [:something_else]}, :file_line) }.to raise_error(ArgumentError, /must be a/i)
|
148
204
|
end
|
149
205
|
end
|
150
206
|
|
@@ -152,8 +208,8 @@ describe 'Slicer, Integration' do
|
|
152
208
|
tag_filter_types = clazz.known_filters.select { |filter| filter.to_s =~ /tag/ }
|
153
209
|
|
154
210
|
tag_filter_types.each do |filter|
|
155
|
-
expect { slicer.slice(@default_file_directory, filter => ['@some_value', [/nested_pattern/]]) }.to_not raise_error
|
156
|
-
expect { slicer.slice(@default_file_directory, filter => ['@some_value', [/nested_pattern/, ['way_too_nested']]]) }.to raise_error(ArgumentError, /cannot.* nested/i)
|
211
|
+
expect { slicer.slice(@default_file_directory, {filter => ['@some_value', [/nested_pattern/]]}, :file_line) }.to_not raise_error
|
212
|
+
expect { slicer.slice(@default_file_directory, {filter => ['@some_value', [/nested_pattern/, ['way_too_nested']]]}, :file_line) }.to raise_error(ArgumentError, /cannot.* nested/i)
|
157
213
|
end
|
158
214
|
end
|
159
215
|
|
@@ -161,11 +217,63 @@ describe 'Slicer, Integration' do
|
|
161
217
|
unknown_filter_type = :unknown_filter
|
162
218
|
options = {unknown_filter_type => 'foo'}
|
163
219
|
|
164
|
-
expect { slicer.slice(@default_file_directory, options) }.to raise_error(ArgumentError, /unknown filter.*#{unknown_filter_type}/i)
|
220
|
+
expect { slicer.slice(@default_file_directory, options, :file_line) }.to raise_error(ArgumentError, /unknown filter.*#{unknown_filter_type}/i)
|
165
221
|
end
|
166
222
|
|
167
223
|
end
|
168
224
|
|
169
225
|
end
|
170
226
|
|
227
|
+
describe "bugs that we don't want to happen again" do
|
228
|
+
|
229
|
+
|
230
|
+
# As a nested directory structure was being traversed for slicing, the extraction algorithm was mangling the
|
231
|
+
# current file path such that it would sometimes attempt to search non-existent locations. Sometimes this
|
232
|
+
# resulted in an exception and sometimes this resulted in files getting silently skipped over.
|
233
|
+
|
234
|
+
|
235
|
+
it 'can handle a realistically nested directory structure' do
|
236
|
+
root_directory = @default_file_directory
|
237
|
+
|
238
|
+
# Outer 'before' hook already makes a root level feature file
|
239
|
+
expected_tests = ["#{test_file}:4"]
|
240
|
+
|
241
|
+
# Adding a nested directory
|
242
|
+
nested_directory_1 = "#{root_directory}/nested_directory_1"
|
243
|
+
FileUtils.mkpath(nested_directory_1)
|
244
|
+
test_file = "#{nested_directory_1}/nested_file_1.feature"
|
245
|
+
File.write(test_file, test_file_text)
|
246
|
+
expected_tests << "#{test_file}:4"
|
247
|
+
test_file = "#{nested_directory_1}/nested_file_2.feature"
|
248
|
+
File.write(test_file, test_file_text)
|
249
|
+
expected_tests << "#{test_file}:4"
|
250
|
+
|
251
|
+
# And another one
|
252
|
+
nested_directory_2 = "#{root_directory}/nested_directory_2"
|
253
|
+
FileUtils.mkpath(nested_directory_2)
|
254
|
+
test_file = "#{nested_directory_2}/nested_file_1.feature"
|
255
|
+
File.write(test_file, test_file_text)
|
256
|
+
expected_tests << "#{test_file}:4"
|
257
|
+
test_file = "#{nested_directory_2}/nested_file_2.feature"
|
258
|
+
File.write(test_file, test_file_text)
|
259
|
+
expected_tests << "#{test_file}:4"
|
260
|
+
|
261
|
+
# And one of them has another directory inside of it
|
262
|
+
doubly_nested_directory = "#{nested_directory_1}/doubly_nested_directory"
|
263
|
+
FileUtils.mkpath(doubly_nested_directory)
|
264
|
+
test_file = "#{doubly_nested_directory}/doubly_nested_file_1.feature"
|
265
|
+
File.write(test_file, test_file_text)
|
266
|
+
expected_tests << "#{test_file}:4"
|
267
|
+
test_file = "#{doubly_nested_directory}/doubly_nested_file_2.feature"
|
268
|
+
File.write(test_file, test_file_text)
|
269
|
+
expected_tests << "#{test_file}:4"
|
270
|
+
|
271
|
+
|
272
|
+
# No problems, no missed files
|
273
|
+
expect { @slice_output = slicer.slice(root_directory, :file_line) }.to_not raise_error
|
274
|
+
expect(@slice_output).to match_array(expected_tests)
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
171
279
|
end
|