cucumber-core 1.3.0 → 1.3.1
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 +4 -4
- data/.rspec +1 -1
- data/.travis.yml +5 -2
- data/HISTORY.md +15 -1
- data/README.md +2 -1
- data/Rakefile +6 -2
- data/lib/cucumber/core/ast/empty_multiline_argument.rb +2 -2
- data/lib/cucumber/core/ast/feature.rb +1 -0
- data/lib/cucumber/core/ast/location.rb +71 -52
- data/lib/cucumber/core/ast/names.rb +0 -5
- data/lib/cucumber/core/ast/step.rb +2 -5
- data/lib/cucumber/core/test/case.rb +16 -2
- data/lib/cucumber/core/test/filters/locations_filter.rb +8 -11
- data/lib/cucumber/core/test/step.rb +0 -5
- data/lib/cucumber/core/version.rb +1 -1
- data/spec/cucumber/core/ast/location_spec.rb +41 -8
- data/spec/cucumber/core/ast/step_spec.rb +0 -10
- data/spec/cucumber/core/test/case_spec.rb +0 -190
- data/spec/cucumber/core/test/filters/locations_filter_spec.rb +314 -30
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25825fee710c84b86d2a37eed2006660a2a60df0
|
4
|
+
data.tar.gz: 4515e946a6558a3aa42509da7380f61782b7e43f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1fa91c0ea24501ab7d57a14c456e39eee87ced703456076e9b8dc16c191697d27fbeab1e75bd85bd769983bdbd5f4d14e80aeafb8d04e12175943203f06a24b
|
7
|
+
data.tar.gz: e4aff4a98a4702419bc43a5818ec3253deb5a075c71fcadb9f10826cbd3376199132cf65ad3b16761b11785e45bfffea5c94180756c033231dcb00fee0e20f1e
|
data/.rspec
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--color
|
1
|
+
--color --tag ~slow
|
data/.travis.yml
CHANGED
data/HISTORY.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
## [In Git](https://github.com/cucumber/cucumber-ruby-core/compare/v1.
|
1
|
+
## [In Git](https://github.com/cucumber/cucumber-ruby-core/compare/v1.3.0...master)
|
2
|
+
|
3
|
+
### New Features
|
4
|
+
|
5
|
+
### Bugfixes
|
6
|
+
|
7
|
+
## [v1.3.1](https://github.com/cucumber/cucumber-ruby-core/compare/v1.3.0...v1.3.1)
|
8
|
+
|
9
|
+
### New Features
|
10
|
+
|
11
|
+
### Bugfixes
|
12
|
+
|
13
|
+
* Speed up location filtering ([99](https://github.com/cucumber/cucumber-ruby-core/issues/99) @mattwynne @akostadinov @brasmusson)
|
14
|
+
|
15
|
+
## [v1.3.0](https://github.com/cucumber/cucumber-ruby-core/compare/v1.2.0...v1.3.0)
|
2
16
|
|
3
17
|
### New Features
|
4
18
|
|
data/README.md
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# cucumber-core
|
2
2
|
|
3
|
+
[](https://gitter.im/cucumber/cucumber-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
3
4
|
[](http://travis-ci.org/cucumber/cucumber-ruby-core)
|
4
5
|
[](https://codeclimate.com/github/cucumber/cucumber-ruby-core)
|
5
6
|
[](https://coveralls.io/r/cucumber/cucumber-ruby-core?branch=master)
|
6
7
|
[](https://gemnasium.com/cucumber/cucumber-ruby-core)
|
7
8
|
|
8
|
-
Cucumber Core is the [inner hexagon](http://alistair.cockburn.us/Hexagonal+architecture) for the [Ruby flavour of Cucumber](https://github.com/cucumber/cucumber).
|
9
|
+
Cucumber Core is the [inner hexagon](http://alistair.cockburn.us/Hexagonal+architecture) for the [Ruby flavour of Cucumber](https://github.com/cucumber/cucumber-ruby).
|
9
10
|
|
10
11
|
It contains the core domain logic to execute Cucumber features. It has no user interface, just a Ruby API. If you're interested in how Cucumber works, or in building other tools that work with Gherkin documents, you've come to the right place.
|
11
12
|
|
data/Rakefile
CHANGED
@@ -8,7 +8,6 @@ $:.unshift File.expand_path("../lib", __FILE__)
|
|
8
8
|
require "rspec/core/rake_task"
|
9
9
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
10
10
|
t.ruby_opts = %w[-r./spec/coverage -w]
|
11
|
-
t.rspec_opts = %w[--color]
|
12
11
|
end
|
13
12
|
|
14
13
|
require_relative 'spec/capture_warnings'
|
@@ -19,6 +18,11 @@ namespace :spec do
|
|
19
18
|
Rake::Task['spec'].invoke
|
20
19
|
end
|
21
20
|
end
|
21
|
+
|
22
|
+
desc "run (slow) performance tests"
|
23
|
+
RSpec::Core::RakeTask.new(:slow) do |t|
|
24
|
+
t.rspec_opts = %w[--tag slow]
|
25
|
+
end
|
22
26
|
end
|
23
27
|
|
24
|
-
task default: ['spec:warnings']
|
28
|
+
task default: ['spec:warnings', 'spec:slow']
|
@@ -3,15 +3,9 @@ require 'cucumber/core/platform'
|
|
3
3
|
module Cucumber
|
4
4
|
module Core
|
5
5
|
module Ast
|
6
|
-
|
7
|
-
WILDCARD = :*
|
6
|
+
IncompatibleLocations = Class.new(StandardError)
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
def_delegator :lines, :include?
|
12
|
-
def_delegator :lines, :line
|
13
|
-
def_delegator :filepath, :same_as?
|
14
|
-
def_delegator :filepath, :filename, :file
|
8
|
+
module Location
|
15
9
|
|
16
10
|
def self.of_caller(additional_depth = 0)
|
17
11
|
from_file_colon_line(*caller[1 + additional_depth])
|
@@ -34,62 +28,100 @@ module Cucumber
|
|
34
28
|
new(file, line)
|
35
29
|
end
|
36
30
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
31
|
+
def self.new(file, raw_lines=nil)
|
32
|
+
file || raise(ArgumentError, "file is mandatory")
|
33
|
+
if raw_lines
|
34
|
+
Precise.new(file, Lines.new(raw_lines))
|
35
|
+
else
|
36
|
+
Wildcard.new(file)
|
37
|
+
end
|
40
38
|
end
|
41
39
|
|
42
|
-
def
|
43
|
-
|
40
|
+
def self.merge(*locations)
|
41
|
+
locations.reduce do |a, b|
|
42
|
+
a + b
|
43
|
+
end
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
class Wildcard < Struct.new(:file)
|
47
|
+
def to_s
|
48
|
+
file
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
def match?(other)
|
52
|
+
other.file == file
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
-
|
55
|
+
def include?(lines)
|
56
|
+
true
|
57
|
+
end
|
56
58
|
end
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
class Precise < Struct.new(:file, :lines)
|
61
|
+
def include?(other_lines)
|
62
|
+
lines.include?(other_lines)
|
63
|
+
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
def line
|
66
|
+
lines.first
|
67
|
+
end
|
65
68
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
+
def match?(other)
|
70
|
+
return false unless other.file == file
|
71
|
+
other.include?(lines)
|
69
72
|
end
|
70
73
|
|
71
74
|
def to_s
|
72
|
-
|
75
|
+
[file, lines.to_s].join(":")
|
76
|
+
end
|
77
|
+
|
78
|
+
def hash
|
79
|
+
self.class.hash ^ to_s.hash
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_str
|
83
|
+
to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def on_line(new_line)
|
87
|
+
Location.new(file, new_line)
|
88
|
+
end
|
89
|
+
|
90
|
+
def +(other)
|
91
|
+
raise IncompatibleLocations if file != other.file
|
92
|
+
Precise.new(file, lines + other.lines)
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspect
|
96
|
+
"<#{self.class}: #{to_s}>"
|
73
97
|
end
|
74
98
|
end
|
75
99
|
|
76
100
|
require 'set'
|
77
101
|
class Lines < Struct.new(:data)
|
78
102
|
protected :data
|
79
|
-
attr_reader :line
|
80
103
|
|
81
104
|
def initialize(raw_data)
|
82
105
|
super Array(raw_data).to_set
|
83
|
-
|
106
|
+
end
|
107
|
+
|
108
|
+
def first
|
109
|
+
data.first
|
84
110
|
end
|
85
111
|
|
86
112
|
def include?(other)
|
87
|
-
return true if (data|other.data).include?(WILDCARD)
|
88
113
|
other.data.subset?(data) || data.subset?(other.data)
|
89
114
|
end
|
90
115
|
|
116
|
+
def +(more_lines)
|
117
|
+
new_data = data + more_lines.data
|
118
|
+
self.class.new(new_data)
|
119
|
+
end
|
120
|
+
|
91
121
|
def to_s
|
92
|
-
|
122
|
+
return first.to_s if data.length == 1
|
123
|
+
return "#{data.min}..#{data.max}" if range?
|
124
|
+
data.to_a.join(":")
|
93
125
|
end
|
94
126
|
|
95
127
|
def inspect
|
@@ -98,20 +130,8 @@ module Cucumber
|
|
98
130
|
|
99
131
|
protected
|
100
132
|
|
101
|
-
def
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
def at_index(idx)
|
106
|
-
data.to_a[idx]
|
107
|
-
end
|
108
|
-
|
109
|
-
def value
|
110
|
-
method :at_index
|
111
|
-
end
|
112
|
-
|
113
|
-
def first_and_last(something)
|
114
|
-
[0, -1].map(&something)
|
133
|
+
def range?
|
134
|
+
data.size == (data.max - data.min + 1)
|
115
135
|
end
|
116
136
|
end
|
117
137
|
end
|
@@ -134,9 +154,8 @@ module Cucumber
|
|
134
154
|
@location
|
135
155
|
end
|
136
156
|
|
137
|
-
def
|
138
|
-
|
139
|
-
queried_locations.any? { |queried_location| queried_location.match? location }
|
157
|
+
def all_locations
|
158
|
+
@all_locations ||= Location.merge([location] + attributes.map { |node| node.all_locations }.flatten)
|
140
159
|
end
|
141
160
|
|
142
161
|
def attributes
|
@@ -56,11 +56,8 @@ module Cucumber
|
|
56
56
|
@outline_step, @language, @location, @comments, @keyword, @name, @multiline_arg = outline_step, language, location, comments, keyword, name, multiline_arg
|
57
57
|
end
|
58
58
|
|
59
|
-
|
60
|
-
|
61
|
-
def match_locations?(queried_locations)
|
62
|
-
return true if self_match_locations?(queried_locations)
|
63
|
-
@outline_step.match_locations?(queried_locations)
|
59
|
+
def all_locations
|
60
|
+
@outline_step.all_locations
|
64
61
|
end
|
65
62
|
|
66
63
|
alias :step_backtrace_line :backtrace_line
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'cucumber/core/test/result'
|
2
2
|
require 'cucumber/core/gherkin/tag_expression'
|
3
|
+
require 'cucumber/core/ast/location'
|
3
4
|
|
4
5
|
module Cucumber
|
5
6
|
module Core
|
@@ -73,8 +74,21 @@ module Cucumber
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def match_locations?(queried_locations)
|
76
|
-
|
77
|
-
|
77
|
+
queried_locations.any? { |queried_location|
|
78
|
+
all_source.any? { |node|
|
79
|
+
node.all_locations.any? { |location|
|
80
|
+
queried_location.match? location
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def all_locations
|
87
|
+
@all_locations ||= Ast::Location.merge(all_source.map(&:all_locations).flatten)
|
88
|
+
end
|
89
|
+
|
90
|
+
def all_source
|
91
|
+
@all_source ||= (source + test_steps.map(&:source)).flatten.uniq
|
78
92
|
end
|
79
93
|
|
80
94
|
def inspect
|
@@ -5,10 +5,10 @@ module Cucumber
|
|
5
5
|
module Test
|
6
6
|
|
7
7
|
# Sorts and filters scenarios based on a list of locations
|
8
|
-
class LocationsFilter < Filter.new(:
|
8
|
+
class LocationsFilter < Filter.new(:filter_locations)
|
9
9
|
|
10
10
|
def test_case(test_case)
|
11
|
-
test_cases << test_case
|
11
|
+
test_cases[test_case.location.file] << test_case
|
12
12
|
self
|
13
13
|
end
|
14
14
|
|
@@ -23,19 +23,16 @@ module Cucumber
|
|
23
23
|
private
|
24
24
|
|
25
25
|
def sorted_test_cases
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
test_case.match_locations?([location])
|
32
|
-
end
|
26
|
+
filter_locations.map { |filter_location|
|
27
|
+
test_cases[filter_location.file].select { |test_case|
|
28
|
+
test_case.all_locations.any? { |location| filter_location.match?(location) }
|
29
|
+
}
|
30
|
+
}.flatten.uniq
|
33
31
|
end
|
34
32
|
|
35
33
|
def test_cases
|
36
|
-
@test_cases ||= []
|
34
|
+
@test_cases ||= Hash.new { |hash, key| hash[key] = [] }
|
37
35
|
end
|
38
|
-
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -47,11 +47,6 @@ module Cucumber
|
|
47
47
|
@action.location
|
48
48
|
end
|
49
49
|
|
50
|
-
def match_locations?(queried_locations)
|
51
|
-
return true if queried_locations.include? location
|
52
|
-
source.any? { |s| s.match_locations?(queried_locations) }
|
53
|
-
end
|
54
|
-
|
55
50
|
def inspect
|
56
51
|
"#<#{self.class}: #{location}>"
|
57
52
|
end
|
@@ -30,14 +30,6 @@ module Cucumber::Core::Ast
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
describe "line" do
|
34
|
-
it "is an integer" do
|
35
|
-
expect(Location.new(file, line).line).to be_kind_of(Integer)
|
36
|
-
expect(Location.new(file, 1..2).line).to be_kind_of(Integer)
|
37
|
-
expect(Location.of_caller.line).to be_kind_of(Integer)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
33
|
describe "to_s" do
|
42
34
|
it "is file:line for a precise location" do
|
43
35
|
expect( Location.new("foo.feature", 12).to_s ).to eq "foo.feature:12"
|
@@ -50,6 +42,10 @@ module Cucumber::Core::Ast
|
|
50
42
|
it "is file:first_line..last_line for a ranged location" do
|
51
43
|
expect( Location.new("foo.feature", 13..19).to_s ).to eq "foo.feature:13..19"
|
52
44
|
end
|
45
|
+
|
46
|
+
it "is file:line:line:line for an arbitrary set of lines" do
|
47
|
+
expect( Location.new("foo.feature", [1,3,5]).to_s ).to eq "foo.feature:1:3:5"
|
48
|
+
end
|
53
49
|
end
|
54
50
|
|
55
51
|
describe "matches" do
|
@@ -120,6 +116,22 @@ module Cucumber::Core::Ast
|
|
120
116
|
expect( range ).not_to be_match(other)
|
121
117
|
end
|
122
118
|
end
|
119
|
+
|
120
|
+
context "an arbitrary list of lines" do
|
121
|
+
let(:location) { Location.new("foo.feature", [1,5,6,7]) }
|
122
|
+
|
123
|
+
it "matches any of the given lines" do
|
124
|
+
[1,5,6,7].each do |line|
|
125
|
+
other = Location.new("foo.feature", line)
|
126
|
+
expect(location).to be_match(other)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it "does not match another line" do
|
131
|
+
other = Location.new("foo.feature", 2)
|
132
|
+
expect(location).not_to be_match(other)
|
133
|
+
end
|
134
|
+
end
|
123
135
|
end
|
124
136
|
|
125
137
|
describe "created from source location" do
|
@@ -160,6 +172,27 @@ module Cucumber::Core::Ast
|
|
160
172
|
end
|
161
173
|
end
|
162
174
|
|
175
|
+
describe ".merge" do
|
176
|
+
it "merges locations from the same file" do
|
177
|
+
file = "test.feature"
|
178
|
+
location = Location.merge(
|
179
|
+
Location.new(file, 1),
|
180
|
+
Location.new(file, 6),
|
181
|
+
Location.new(file, 7)
|
182
|
+
)
|
183
|
+
expect(location.to_s).to eq "test.feature:1:6:7"
|
184
|
+
end
|
185
|
+
|
186
|
+
it "raises an error when the locations are from different files" do
|
187
|
+
expect {
|
188
|
+
Location.merge(
|
189
|
+
Location.new("one.feature", 1),
|
190
|
+
Location.new("two.feature", 1)
|
191
|
+
)
|
192
|
+
}.to raise_error(IncompatibleLocations)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
163
196
|
end
|
164
197
|
end
|
165
198
|
|
@@ -148,16 +148,6 @@ module Cucumber
|
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
|
-
describe "matching location" do
|
152
|
-
let(:location) { double }
|
153
|
-
|
154
|
-
it "also match the outline steps location" do
|
155
|
-
allow( location).to receive(:any?).and_return(nil)
|
156
|
-
expect( outline_step ).to receive(:match_locations?).with(location)
|
157
|
-
step.match_locations?(location)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
151
|
describe "backtrace line" do
|
162
152
|
let(:outline_step) { OutlineStep.new(double, "path/file.feature:5", double, "Given ", "this step <state>", double) }
|
163
153
|
let(:step) { ExpandedOutlineStep.new(outline_step, double, "path/file.feature:10", double, "Given ", "this step passes", double) }
|
@@ -232,196 +232,6 @@ module Cucumber
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
-
describe "matching location" do
|
236
|
-
let(:file) { 'features/path/to/the.feature' }
|
237
|
-
let(:test_cases) do
|
238
|
-
receiver = double.as_null_object
|
239
|
-
result = []
|
240
|
-
allow( receiver ).to receive(:test_case) { |test_case| result << test_case }
|
241
|
-
compile [source], receiver
|
242
|
-
result
|
243
|
-
end
|
244
|
-
|
245
|
-
context "for a scenario" do
|
246
|
-
let(:source) do
|
247
|
-
Gherkin::Document.new(file, <<-END.unindent)
|
248
|
-
Feature:
|
249
|
-
|
250
|
-
Scenario: one
|
251
|
-
Given one a
|
252
|
-
|
253
|
-
# comment
|
254
|
-
@tags
|
255
|
-
Scenario: two
|
256
|
-
Given two a
|
257
|
-
And two b
|
258
|
-
|
259
|
-
Scenario: three
|
260
|
-
Given three b
|
261
|
-
|
262
|
-
Scenario: with docstring
|
263
|
-
Given a docstring
|
264
|
-
"""
|
265
|
-
this is a docstring
|
266
|
-
"""
|
267
|
-
|
268
|
-
Scenario: with a table
|
269
|
-
Given a table
|
270
|
-
| a | b |
|
271
|
-
| 1 | 2 |
|
272
|
-
|
273
|
-
Scenario: empty
|
274
|
-
END
|
275
|
-
end
|
276
|
-
|
277
|
-
let(:test_case) do
|
278
|
-
test_cases.find { |c| c.name == 'two' }
|
279
|
-
end
|
280
|
-
|
281
|
-
it 'matches the precise location of the scenario' do
|
282
|
-
location = Ast::Location.new(file, 8)
|
283
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
284
|
-
end
|
285
|
-
|
286
|
-
it 'matches the precise location of an empty scenario' do
|
287
|
-
empty_scenario_test_case = test_cases.find { |c| c.name == 'empty' }
|
288
|
-
location = Ast::Location.new(file, 26)
|
289
|
-
expect( empty_scenario_test_case.match_locations?([location]) ).to be_truthy
|
290
|
-
end
|
291
|
-
|
292
|
-
it 'matches multiple locations' do
|
293
|
-
good_location = Ast::Location.new(file, 8)
|
294
|
-
bad_location = Ast::Location.new(file, 5)
|
295
|
-
expect( test_case.match_locations?([good_location, bad_location]) ).to be_truthy
|
296
|
-
end
|
297
|
-
|
298
|
-
it 'matches a location on the last step of the scenario' do
|
299
|
-
location = Ast::Location.new(file, 10)
|
300
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
301
|
-
end
|
302
|
-
|
303
|
-
it "matches a location on the scenario's comment" do
|
304
|
-
location = Ast::Location.new(file, 6)
|
305
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
306
|
-
end
|
307
|
-
|
308
|
-
it "matches a location on the scenario's tags" do
|
309
|
-
location = Ast::Location.new(file, 7)
|
310
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
311
|
-
end
|
312
|
-
|
313
|
-
it "doesn't match a location after the last step of the scenario" do
|
314
|
-
location = Ast::Location.new(file, 11)
|
315
|
-
expect( test_case.match_locations?([location]) ).to be_falsey
|
316
|
-
end
|
317
|
-
|
318
|
-
it "doesn't match a location before the scenario" do
|
319
|
-
location = Ast::Location.new(file, 5)
|
320
|
-
expect( test_case.match_locations?([location]) ).to be_falsey
|
321
|
-
end
|
322
|
-
|
323
|
-
context "with a docstring" do
|
324
|
-
let(:test_case) do
|
325
|
-
test_cases.find { |c| c.name == 'with docstring' }
|
326
|
-
end
|
327
|
-
|
328
|
-
it "matches a location at the start the docstring" do
|
329
|
-
location = Ast::Location.new(file, 17)
|
330
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
331
|
-
end
|
332
|
-
|
333
|
-
it "matches a location in the middle of the docstring" do
|
334
|
-
location = Ast::Location.new(file, 18)
|
335
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
336
|
-
end
|
337
|
-
|
338
|
-
it "matches a location at the end of the docstring" do
|
339
|
-
location = Ast::Location.new(file, 19)
|
340
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
341
|
-
end
|
342
|
-
|
343
|
-
it "does not match a location after the docstring" do
|
344
|
-
location = Ast::Location.new(file, 20)
|
345
|
-
expect( test_case.match_locations?([location]) ).to be_falsy
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
|
-
context "with a table" do
|
350
|
-
let(:test_case) do
|
351
|
-
test_cases.find { |c| c.name == 'with a table' }
|
352
|
-
end
|
353
|
-
|
354
|
-
it "matches a location on the first table row" do
|
355
|
-
location = Ast::Location.new(file, 23)
|
356
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
357
|
-
end
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
context "for a scenario outline" do
|
362
|
-
let(:source) do
|
363
|
-
Gherkin::Document.new(file, <<-END.unindent)
|
364
|
-
Feature:
|
365
|
-
|
366
|
-
Scenario: one
|
367
|
-
Given one a
|
368
|
-
|
369
|
-
# comment on line 6
|
370
|
-
@tags-on-line-7
|
371
|
-
Scenario Outline: two
|
372
|
-
Given two a
|
373
|
-
And two <arg>
|
374
|
-
|
375
|
-
# comment on line 12
|
376
|
-
@tags-on-line-13
|
377
|
-
Examples: x1
|
378
|
-
| arg |
|
379
|
-
| b |
|
380
|
-
|
381
|
-
Examples: x2
|
382
|
-
| arg |
|
383
|
-
| c |
|
384
|
-
|
385
|
-
Scenario: three
|
386
|
-
Given three b
|
387
|
-
END
|
388
|
-
end
|
389
|
-
|
390
|
-
let(:test_case) do
|
391
|
-
test_cases.find { |c| c.name == "two, x1 (#1)" }
|
392
|
-
end
|
393
|
-
|
394
|
-
it 'matches the precise location of the scenario outline examples table row' do
|
395
|
-
location = Ast::Location.new(file, 16)
|
396
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
397
|
-
end
|
398
|
-
|
399
|
-
it 'matches a location on a step of the scenario outline' do
|
400
|
-
location = Ast::Location.new(file, 10)
|
401
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
402
|
-
end
|
403
|
-
|
404
|
-
it "matches a location on the scenario outline's comment" do
|
405
|
-
location = Ast::Location.new(file, 6)
|
406
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
407
|
-
end
|
408
|
-
|
409
|
-
it "matches a location on the scenario outline's tags" do
|
410
|
-
location = Ast::Location.new(file, 7)
|
411
|
-
expect( test_case.match_locations?([location]) ).to be_truthy
|
412
|
-
end
|
413
|
-
|
414
|
-
it "doesn't match a location after the last row of the examples table" do
|
415
|
-
location = Ast::Location.new(file, 17)
|
416
|
-
expect( test_case.match_locations?([location]) ).to be_falsey
|
417
|
-
end
|
418
|
-
|
419
|
-
it "doesn't match a location before the scenario outline" do
|
420
|
-
location = Ast::Location.new(file, 5)
|
421
|
-
expect( test_case.match_locations?([location]) ).to be_falsey
|
422
|
-
end
|
423
|
-
end
|
424
|
-
end
|
425
235
|
end
|
426
236
|
end
|
427
237
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'cucumber/core/gherkin/writer'
|
2
3
|
require 'cucumber/core'
|
3
4
|
require 'cucumber/core/test/filters/locations_filter'
|
4
5
|
require 'timeout'
|
6
|
+
require 'cucumber/core/ast/location'
|
5
7
|
|
6
|
-
module Cucumber::Core
|
7
|
-
describe LocationsFilter do
|
8
|
+
module Cucumber::Core
|
9
|
+
describe Test::LocationsFilter do
|
8
10
|
include Cucumber::Core::Gherkin::Writer
|
9
11
|
include Cucumber::Core
|
10
12
|
|
@@ -26,35 +28,317 @@ module Cucumber::Core::Test
|
|
26
28
|
|
27
29
|
it "sorts by the given locations" do
|
28
30
|
locations = [
|
29
|
-
|
30
|
-
|
31
|
+
Ast::Location.new('features/test.feature', 6),
|
32
|
+
Ast::Location.new('features/test.feature', 3)
|
31
33
|
]
|
32
|
-
filter = LocationsFilter.new(locations)
|
34
|
+
filter = Test::LocationsFilter.new(locations)
|
33
35
|
compile [doc], receiver, [filter]
|
34
|
-
expect(receiver.test_case_locations).to eq
|
36
|
+
expect(receiver.test_case_locations).to eq locations
|
35
37
|
end
|
36
38
|
|
37
39
|
it "works with wildcard locations" do
|
38
40
|
locations = [
|
39
|
-
|
41
|
+
Ast::Location.new('features/test.feature')
|
40
42
|
]
|
41
|
-
filter = LocationsFilter.new(locations)
|
43
|
+
filter = Test::LocationsFilter.new(locations)
|
42
44
|
compile [doc], receiver, [filter]
|
43
|
-
expect(receiver.test_case_locations).to eq [
|
45
|
+
expect(receiver.test_case_locations).to eq [
|
46
|
+
Ast::Location.new('features/test.feature', 3),
|
47
|
+
Ast::Location.new('features/test.feature', 6)
|
48
|
+
]
|
44
49
|
end
|
45
50
|
|
46
51
|
it "filters out scenarios that don't match" do
|
47
52
|
locations = [
|
48
|
-
|
53
|
+
Ast::Location.new('features/test.feature', 3)
|
49
54
|
]
|
50
|
-
filter = LocationsFilter.new(locations)
|
55
|
+
filter = Test::LocationsFilter.new(locations)
|
51
56
|
compile [doc], receiver, [filter]
|
52
|
-
expect(receiver.test_case_locations).to eq
|
57
|
+
expect(receiver.test_case_locations).to eq locations
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "matching location" do
|
61
|
+
let(:file) { 'features/path/to/the.feature' }
|
62
|
+
|
63
|
+
let(:test_cases) do
|
64
|
+
receiver = double.as_null_object
|
65
|
+
result = []
|
66
|
+
allow(receiver).to receive(:test_case) { |test_case| result << test_case }
|
67
|
+
compile [doc], receiver
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
context "for a scenario" do
|
72
|
+
let(:doc) do
|
73
|
+
Gherkin::Document.new(file, <<-END)
|
74
|
+
Feature:
|
75
|
+
|
76
|
+
Scenario: one
|
77
|
+
Given one a
|
78
|
+
|
79
|
+
# comment
|
80
|
+
@tags
|
81
|
+
Scenario: two
|
82
|
+
Given two a
|
83
|
+
And two b
|
84
|
+
|
85
|
+
Scenario: three
|
86
|
+
Given three b
|
87
|
+
|
88
|
+
Scenario: with docstring
|
89
|
+
Given a docstring
|
90
|
+
"""
|
91
|
+
this is a docstring
|
92
|
+
"""
|
93
|
+
|
94
|
+
Scenario: with a table
|
95
|
+
Given a table
|
96
|
+
| a | b |
|
97
|
+
| 1 | 2 |
|
98
|
+
|
99
|
+
Scenario: empty
|
100
|
+
END
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_case_named(name)
|
104
|
+
test_cases.find { |c| c.name == name }
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'matches the precise location of the scenario' do
|
108
|
+
location = test_case_named('two').location
|
109
|
+
filter = Test::LocationsFilter.new([location])
|
110
|
+
compile [doc], receiver, [filter]
|
111
|
+
expect(receiver.test_case_locations).to eq [test_case_named('two').location]
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'matches the precise location of an empty scenario' do
|
115
|
+
location = test_case_named('empty').location
|
116
|
+
filter = Test::LocationsFilter.new([location])
|
117
|
+
compile [doc], receiver, [filter]
|
118
|
+
expect(receiver.test_case_locations).to eq [test_case_named('empty').location]
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'matches multiple locations' do
|
122
|
+
good_location = Ast::Location.new(file, 8)
|
123
|
+
bad_location = Ast::Location.new(file, 5)
|
124
|
+
filter = Test::LocationsFilter.new([good_location, bad_location])
|
125
|
+
compile [doc], receiver, [filter]
|
126
|
+
expect(receiver.test_case_locations).to eq [test_case_named('two').location]
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'matches a location on the last step of the scenario' do
|
130
|
+
location = Ast::Location.new(file, 10)
|
131
|
+
filter = Test::LocationsFilter.new([location])
|
132
|
+
compile [doc], receiver, [filter]
|
133
|
+
expect(receiver.test_case_locations).to eq [test_case_named('two').location]
|
134
|
+
end
|
135
|
+
|
136
|
+
it "matches a location on the scenario's comment" do
|
137
|
+
location = Ast::Location.new(file, 6)
|
138
|
+
filter = Test::LocationsFilter.new([location])
|
139
|
+
compile [doc], receiver, [filter]
|
140
|
+
expect(receiver.test_case_locations).to eq [test_case_named('two').location]
|
141
|
+
end
|
142
|
+
|
143
|
+
it "matches a location on the scenario's tags" do
|
144
|
+
location = Ast::Location.new(file, 7)
|
145
|
+
filter = Test::LocationsFilter.new([location])
|
146
|
+
compile [doc], receiver, [filter]
|
147
|
+
expect(receiver.test_case_locations).to eq [test_case_named('two').location]
|
148
|
+
end
|
149
|
+
|
150
|
+
it "doesn't match a location after the last step of the scenario" do
|
151
|
+
location = Ast::Location.new(file, 11)
|
152
|
+
filter = Test::LocationsFilter.new([location])
|
153
|
+
compile [doc], receiver, [filter]
|
154
|
+
expect(receiver.test_case_locations).to eq []
|
155
|
+
end
|
156
|
+
|
157
|
+
it "doesn't match a location before the scenario" do
|
158
|
+
location = Ast::Location.new(file, 5)
|
159
|
+
filter = Test::LocationsFilter.new([location])
|
160
|
+
compile [doc], receiver, [filter]
|
161
|
+
expect(receiver.test_case_locations).to eq []
|
162
|
+
end
|
163
|
+
|
164
|
+
context "with a docstring" do
|
165
|
+
let(:test_case) do
|
166
|
+
test_cases.find { |c| c.name == 'with docstring' }
|
167
|
+
end
|
168
|
+
|
169
|
+
it "matches a location at the start the docstring" do
|
170
|
+
location = Ast::Location.new(file, 17)
|
171
|
+
filter = Test::LocationsFilter.new([location])
|
172
|
+
compile [doc], receiver, [filter]
|
173
|
+
expect(receiver.test_case_locations).to eq [test_case.location]
|
174
|
+
end
|
175
|
+
|
176
|
+
it "matches a location in the middle of the docstring" do
|
177
|
+
location = Ast::Location.new(file, 18)
|
178
|
+
filter = Test::LocationsFilter.new([location])
|
179
|
+
compile [doc], receiver, [filter]
|
180
|
+
expect(receiver.test_case_locations).to eq [test_case.location]
|
181
|
+
end
|
182
|
+
|
183
|
+
it "matches a location at the end of the docstring" do
|
184
|
+
location = Ast::Location.new(file, 19)
|
185
|
+
filter = Test::LocationsFilter.new([location])
|
186
|
+
compile [doc], receiver, [filter]
|
187
|
+
expect(receiver.test_case_locations).to eq [test_case.location]
|
188
|
+
end
|
189
|
+
|
190
|
+
it "does not match a location after the docstring" do
|
191
|
+
location = Ast::Location.new(file, 20)
|
192
|
+
filter = Test::LocationsFilter.new([location])
|
193
|
+
compile [doc], receiver, [filter]
|
194
|
+
expect(receiver.test_case_locations).to eq []
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "with a table" do
|
199
|
+
let(:test_case) do
|
200
|
+
test_cases.find { |c| c.name == 'with a table' }
|
201
|
+
end
|
202
|
+
|
203
|
+
it "matches a location on the first table row" do
|
204
|
+
location = Ast::Location.new(file, 23)
|
205
|
+
expect( test_case.match_locations?([location]) ).to be_truthy
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "with duplicate locations in the filter" do
|
210
|
+
it "matches each test case only once" do
|
211
|
+
location_tc_two = test_case_named('two').location
|
212
|
+
location_tc_one = test_case_named('one').location
|
213
|
+
location_last_step_tc_two = Ast::Location.new(file, 10)
|
214
|
+
filter = Test::LocationsFilter.new([location_tc_two, location_tc_one, location_last_step_tc_two])
|
215
|
+
compile [doc], receiver, [filter]
|
216
|
+
expect(receiver.test_case_locations).to eq [test_case_named('two').location, location_tc_one = test_case_named('one').location]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "for a scenario outline" do
|
222
|
+
let(:doc) do
|
223
|
+
Gherkin::Document.new(file, <<-END)
|
224
|
+
Feature:
|
225
|
+
|
226
|
+
Scenario: one
|
227
|
+
Given one a
|
228
|
+
|
229
|
+
# comment on line 6
|
230
|
+
@tags-on-line-7
|
231
|
+
Scenario Outline: two
|
232
|
+
Given two a
|
233
|
+
And two <arg>
|
234
|
+
"""
|
235
|
+
docstring
|
236
|
+
"""
|
237
|
+
|
238
|
+
# comment on line 15
|
239
|
+
@tags-on-line-16
|
240
|
+
Examples: x1
|
241
|
+
| arg |
|
242
|
+
| b |
|
243
|
+
|
244
|
+
Examples: x2
|
245
|
+
| arg |
|
246
|
+
| c |
|
247
|
+
| d |
|
248
|
+
|
249
|
+
Scenario: three
|
250
|
+
Given three b
|
251
|
+
END
|
252
|
+
end
|
253
|
+
|
254
|
+
let(:test_case) do
|
255
|
+
test_cases.find { |c| c.name == "two, x1 (#1)" }
|
256
|
+
end
|
257
|
+
|
258
|
+
it "matches row location to the test case of the row" do
|
259
|
+
locations = [
|
260
|
+
Ast::Location.new(file, 19),
|
261
|
+
]
|
262
|
+
filter = Test::LocationsFilter.new(locations)
|
263
|
+
compile [doc], receiver, [filter]
|
264
|
+
expect(receiver.test_case_locations).to eq [test_case.location]
|
265
|
+
end
|
266
|
+
|
267
|
+
it "matches examples location with all test cases of the table" do
|
268
|
+
locations = [
|
269
|
+
Ast::Location.new(file, 21),
|
270
|
+
]
|
271
|
+
filter = Test::LocationsFilter.new(locations)
|
272
|
+
compile [doc], receiver, [filter]
|
273
|
+
expect(receiver.test_case_locations.map(&:line)).to eq [23, 24]
|
274
|
+
end
|
275
|
+
|
276
|
+
it "matches outline location with the all test cases of all the tables" do
|
277
|
+
locations = [
|
278
|
+
Ast::Location.new(file, 8),
|
279
|
+
]
|
280
|
+
filter = Test::LocationsFilter.new(locations)
|
281
|
+
compile [doc], receiver, [filter]
|
282
|
+
expect(receiver.test_case_locations.map(&:line)).to eq [19, 23, 24]
|
283
|
+
end
|
284
|
+
|
285
|
+
it "matches outline step location the all test cases of all the tables" do
|
286
|
+
locations = [
|
287
|
+
Ast::Location.new(file, 10)
|
288
|
+
]
|
289
|
+
filter = Test::LocationsFilter.new(locations)
|
290
|
+
compile [doc], receiver, [filter]
|
291
|
+
expect(receiver.test_case_locations.map(&:line)).to eq [19, 23, 24]
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'matches a location on a step of the scenario outline' do
|
295
|
+
location = Ast::Location.new(file, 10)
|
296
|
+
filter = Test::LocationsFilter.new([location])
|
297
|
+
compile [doc], receiver, [filter]
|
298
|
+
expect(receiver.test_case_locations).to include test_case.location
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'matches a location on the docstring of a step of the scenario outline' do
|
302
|
+
location = Ast::Location.new(file, 12)
|
303
|
+
filter = Test::LocationsFilter.new([location])
|
304
|
+
compile [doc], receiver, [filter]
|
305
|
+
expect(receiver.test_case_locations).to include test_case.location
|
306
|
+
end
|
307
|
+
|
308
|
+
it "matches a location on the scenario outline's comment" do
|
309
|
+
location = Ast::Location.new(file, 6)
|
310
|
+
filter = Test::LocationsFilter.new([location])
|
311
|
+
compile [doc], receiver, [filter]
|
312
|
+
expect(receiver.test_case_locations).to include test_case.location
|
313
|
+
end
|
314
|
+
|
315
|
+
it "matches a location on the scenario outline's tags" do
|
316
|
+
location = Ast::Location.new(file, 7)
|
317
|
+
filter = Test::LocationsFilter.new([location])
|
318
|
+
compile [doc], receiver, [filter]
|
319
|
+
expect(receiver.test_case_locations).to include test_case.location
|
320
|
+
end
|
321
|
+
|
322
|
+
it "doesn't match a location after the last row of the examples table" do
|
323
|
+
location = Ast::Location.new(file, 20)
|
324
|
+
filter = Test::LocationsFilter.new([location])
|
325
|
+
compile [doc], receiver, [filter]
|
326
|
+
expect(receiver.test_case_locations).to eq []
|
327
|
+
end
|
328
|
+
|
329
|
+
it "doesn't match a location before the scenario outline" do
|
330
|
+
location = Ast::Location.new(file, 5)
|
331
|
+
filter = Test::LocationsFilter.new([location])
|
332
|
+
compile [doc], receiver, [filter]
|
333
|
+
expect(receiver.test_case_locations).to eq []
|
334
|
+
end
|
335
|
+
end
|
53
336
|
end
|
54
337
|
|
55
|
-
|
56
|
-
|
57
|
-
|
338
|
+
context "under load", slow: true do
|
339
|
+
num_features = 50
|
340
|
+
num_scenarios_per_feature = 50
|
341
|
+
|
58
342
|
let(:docs) do
|
59
343
|
(1..num_features).map do |i|
|
60
344
|
gherkin("features/test_#{i}.feature") do
|
@@ -74,14 +358,14 @@ module Cucumber::Core::Test
|
|
74
358
|
(1..num_locations).map do |i|
|
75
359
|
(1..num_scenarios_per_feature).map do |j|
|
76
360
|
line = 3 + (j - 1) * 3
|
77
|
-
|
361
|
+
Ast::Location.new("features/test_#{i}.feature", line)
|
78
362
|
end
|
79
363
|
end.flatten
|
80
364
|
end
|
81
365
|
|
82
366
|
max_duration_ms = 10000
|
83
367
|
it "filters #{num_features * num_scenarios_per_feature} test cases within #{max_duration_ms}ms" do
|
84
|
-
filter = LocationsFilter.new(locations)
|
368
|
+
filter = Test::LocationsFilter.new(locations)
|
85
369
|
Timeout.timeout(max_duration_ms / 1000.0) do
|
86
370
|
compile docs, receiver, [filter]
|
87
371
|
end
|
@@ -89,23 +373,23 @@ module Cucumber::Core::Test
|
|
89
373
|
end
|
90
374
|
|
91
375
|
end
|
376
|
+
end
|
92
377
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
def done
|
99
|
-
end
|
378
|
+
class SpyReceiver
|
379
|
+
def test_case(test_case)
|
380
|
+
test_cases << test_case
|
381
|
+
end
|
100
382
|
|
101
|
-
|
102
|
-
|
103
|
-
end
|
383
|
+
def done
|
384
|
+
end
|
104
385
|
|
105
|
-
|
106
|
-
|
107
|
-
|
386
|
+
def test_case_locations
|
387
|
+
test_cases.map(&:location)
|
388
|
+
end
|
108
389
|
|
390
|
+
def test_cases
|
391
|
+
@test_cases ||= []
|
109
392
|
end
|
393
|
+
|
110
394
|
end
|
111
395
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cucumber-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aslak Hellesøy
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2015-
|
15
|
+
date: 2015-12-17 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: gherkin3
|
@@ -219,7 +219,7 @@ rubyforge_project:
|
|
219
219
|
rubygems_version: 2.2.2
|
220
220
|
signing_key:
|
221
221
|
specification_version: 4
|
222
|
-
summary: cucumber-core-1.3.
|
222
|
+
summary: cucumber-core-1.3.1
|
223
223
|
test_files:
|
224
224
|
- spec/capture_warnings.rb
|
225
225
|
- spec/coverage.rb
|