cucumber-core 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Chat with us](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cucumber/cucumber-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
3
4
|
[![Build Status](https://secure.travis-ci.org/cucumber/cucumber-ruby-core.png)](http://travis-ci.org/cucumber/cucumber-ruby-core)
|
4
5
|
[![Code Climate](https://codeclimate.com/github/cucumber/cucumber-ruby-core.png)](https://codeclimate.com/github/cucumber/cucumber-ruby-core)
|
5
6
|
[![Coverage Status](https://coveralls.io/repos/cucumber/cucumber-ruby-core/badge.png?branch=master)](https://coveralls.io/r/cucumber/cucumber-ruby-core?branch=master)
|
6
7
|
[![Dependency Status](https://gemnasium.com/cucumber/cucumber-ruby-core.png)](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
|