cucumber-core 0.1.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/.coveralls.yml +1 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +9 -0
- data/Rakefile +24 -0
- data/cucumber-core.gemspec +32 -0
- data/lib/cucumber/core.rb +37 -0
- data/lib/cucumber/core/ast.rb +13 -0
- data/lib/cucumber/core/ast/background.rb +33 -0
- data/lib/cucumber/core/ast/comment.rb +17 -0
- data/lib/cucumber/core/ast/data_table.rb +326 -0
- data/lib/cucumber/core/ast/describes_itself.rb +16 -0
- data/lib/cucumber/core/ast/doc_string.rb +83 -0
- data/lib/cucumber/core/ast/empty_background.rb +12 -0
- data/lib/cucumber/core/ast/examples_table.rb +95 -0
- data/lib/cucumber/core/ast/feature.rb +62 -0
- data/lib/cucumber/core/ast/location.rb +140 -0
- data/lib/cucumber/core/ast/multiline_argument.rb +33 -0
- data/lib/cucumber/core/ast/names.rb +19 -0
- data/lib/cucumber/core/ast/outline_step.rb +51 -0
- data/lib/cucumber/core/ast/scenario.rb +43 -0
- data/lib/cucumber/core/ast/scenario_outline.rb +44 -0
- data/lib/cucumber/core/ast/step.rb +38 -0
- data/lib/cucumber/core/ast/tag.rb +14 -0
- data/lib/cucumber/core/compiler.rb +136 -0
- data/lib/cucumber/core/gherkin/ast_builder.rb +315 -0
- data/lib/cucumber/core/gherkin/document.rb +20 -0
- data/lib/cucumber/core/gherkin/parser.rb +45 -0
- data/lib/cucumber/core/gherkin/writer.rb +220 -0
- data/lib/cucumber/core/gherkin/writer/helpers.rb +178 -0
- data/lib/cucumber/core/platform.rb +30 -0
- data/lib/cucumber/core/test/case.rb +143 -0
- data/lib/cucumber/core/test/filters.rb +48 -0
- data/lib/cucumber/core/test/filters/tag_filter.rb +110 -0
- data/lib/cucumber/core/test/hook_compiler.rb +109 -0
- data/lib/cucumber/core/test/mapper.rb +56 -0
- data/lib/cucumber/core/test/mapping.rb +67 -0
- data/lib/cucumber/core/test/result.rb +191 -0
- data/lib/cucumber/core/test/runner.rb +149 -0
- data/lib/cucumber/core/test/step.rb +69 -0
- data/lib/cucumber/core/test/timer.rb +31 -0
- data/lib/cucumber/core/version.rb +9 -0
- data/lib/cucumber/initializer.rb +18 -0
- data/spec/capture_warnings.rb +68 -0
- data/spec/coverage.rb +10 -0
- data/spec/cucumber/core/ast/data_table_spec.rb +139 -0
- data/spec/cucumber/core/ast/doc_string_spec.rb +77 -0
- data/spec/cucumber/core/ast/examples_table_spec.rb +87 -0
- data/spec/cucumber/core/ast/location_spec.rb +105 -0
- data/spec/cucumber/core/ast/outline_step_spec.rb +77 -0
- data/spec/cucumber/core/ast/step_spec.rb +44 -0
- data/spec/cucumber/core/compiler_spec.rb +249 -0
- data/spec/cucumber/core/gherkin/parser_spec.rb +182 -0
- data/spec/cucumber/core/gherkin/writer_spec.rb +332 -0
- data/spec/cucumber/core/test/case_spec.rb +416 -0
- data/spec/cucumber/core/test/hook_compiler_spec.rb +78 -0
- data/spec/cucumber/core/test/mapper_spec.rb +68 -0
- data/spec/cucumber/core/test/mapping_spec.rb +103 -0
- data/spec/cucumber/core/test/result_spec.rb +178 -0
- data/spec/cucumber/core/test/runner_spec.rb +265 -0
- data/spec/cucumber/core/test/step_spec.rb +58 -0
- data/spec/cucumber/core/test/timer_spec.rb +13 -0
- data/spec/cucumber/core_spec.rb +419 -0
- data/spec/cucumber/initializer_spec.rb +49 -0
- metadata +221 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'cucumber/core/ast/location'
|
2
|
+
|
3
|
+
module Cucumber::Core::Ast
|
4
|
+
describe Location do
|
5
|
+
let(:line) { 12 }
|
6
|
+
let(:file) { "foo.feature" }
|
7
|
+
|
8
|
+
describe "equality" do
|
9
|
+
it "is equal to another Location on the same line of the same file" do
|
10
|
+
one_location = Location.new(file, line)
|
11
|
+
another_location = Location.new(file, line)
|
12
|
+
expect( one_location ).to eq another_location
|
13
|
+
end
|
14
|
+
|
15
|
+
it "is not equal to a wild card of the same file" do
|
16
|
+
expect( Location.new(file, line) ).not_to eq Location.new(file)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "to_s" do
|
21
|
+
it "is file:line for a precise location" do
|
22
|
+
expect( Location.new("foo.feature", 12).to_s ).to eq "foo.feature:12"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "is file for a wildcard location" do
|
26
|
+
expect( Location.new("foo.feature").to_s ).to eq "foo.feature"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is file:first_line..last_line for a ranged location" do
|
30
|
+
expect( Location.new("foo.feature", 13..19).to_s ).to eq "foo.feature:13..19"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "matches" do
|
35
|
+
let(:matching) { Location.new(file, line) }
|
36
|
+
let(:same_file_other_line) { Location.new(file, double) }
|
37
|
+
let(:not_matching) { Location.new(other_file, line) }
|
38
|
+
let(:other_file) { double }
|
39
|
+
|
40
|
+
context 'a precise location' do
|
41
|
+
let(:precise) { Location.new(file, line) }
|
42
|
+
|
43
|
+
it "matches a precise location of the same file and line" do
|
44
|
+
expect( matching ).to be_match(precise)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "does not match a precise location on a differnt line in the same file" do
|
48
|
+
expect( matching ).not_to be_match(same_file_other_line)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'a wildcard' do
|
54
|
+
let(:wildcard) { Location.new(file) }
|
55
|
+
|
56
|
+
it "matches any location with the same filename" do
|
57
|
+
expect( wildcard ).to be_match(matching)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "is matched by any location of the same file" do
|
61
|
+
expect( matching ).to be_match(wildcard)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "does not match a location in a different file" do
|
65
|
+
expect( wildcard ).not_to be_match(not_matching)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'a range wildcard' do
|
70
|
+
let(:range) { Location.new("foo.feature", 13..17) }
|
71
|
+
|
72
|
+
it "matches the first line in the same file" do
|
73
|
+
other = Location.new("foo.feature", 13)
|
74
|
+
expect( range ).to be_match(other)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "matches a line within the docstring in the same file" do
|
78
|
+
other = Location.new("foo.feature", 15)
|
79
|
+
expect( range ).to be_match(other)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "is matched by a line within the docstring in the same file" do
|
83
|
+
other = Location.new("foo.feature", 15)
|
84
|
+
expect( other ).to be_match(range)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "matches a wildcard in the same file" do
|
88
|
+
wildcard = Location.new("foo.feature")
|
89
|
+
expect( range ).to be_match(wildcard)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "does not match a location outside of the range" do
|
93
|
+
other = Location.new("foo.feature", 18)
|
94
|
+
expect( range ).not_to be_match(other)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "does not match a location in another file" do
|
98
|
+
other = Location.new("bar.feature", 13)
|
99
|
+
expect( range ).not_to be_match(other)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'cucumber/core/ast/outline_step'
|
2
|
+
require 'cucumber/core/ast/examples_table'
|
3
|
+
require 'cucumber/core/ast/data_table'
|
4
|
+
require 'cucumber/core/ast/doc_string'
|
5
|
+
|
6
|
+
module Cucumber
|
7
|
+
module Core
|
8
|
+
module Ast
|
9
|
+
describe OutlineStep do
|
10
|
+
let(:outline_step) { OutlineStep.new(language, location, keyword, name) }
|
11
|
+
let(:language) { double }
|
12
|
+
let(:location) { double }
|
13
|
+
let(:keyword) { double }
|
14
|
+
let(:name) { 'anything' }
|
15
|
+
|
16
|
+
describe 'location' do
|
17
|
+
it "has a location" do
|
18
|
+
expect( outline_step ).to respond_to(:location)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'knows the file and line' do
|
22
|
+
location.stub(:to_s) { 'file_name:8' }
|
23
|
+
expect( outline_step.file_colon_line ).to eq 'file_name:8'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "converting to a Step" do
|
28
|
+
context "a single argument in the name" do
|
29
|
+
let(:name) { 'a <color> cucumber' }
|
30
|
+
|
31
|
+
it "replaces the argument" do
|
32
|
+
row = ExamplesTable::Row.new({'color' => 'green'}, 1, location)
|
33
|
+
expect( outline_step.to_step(row).name ).to eq 'a green cucumber'
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the step has a DataTable" do
|
39
|
+
let(:outline_step) { OutlineStep.new(language, location, keyword, name, table) }
|
40
|
+
let(:name) { "anything" }
|
41
|
+
let(:table) { DataTable.new([['x', 'y'],['a', 'a <arg>']], Location.new('foo.feature', 23)) }
|
42
|
+
|
43
|
+
it "replaces the arguments in the DataTable" do
|
44
|
+
visitor = double
|
45
|
+
visitor.stub(:step).and_yield
|
46
|
+
expect( visitor ).to receive(:table) do |data_table| # TODO: rename this message to :data_table
|
47
|
+
expect( data_table.raw ).to eq [['x', 'y'], ['a', 'a replacement']]
|
48
|
+
end
|
49
|
+
row = ExamplesTable::Row.new({'arg' => 'replacement'}, 1, location)
|
50
|
+
step = outline_step.to_step(row)
|
51
|
+
step.describe_to(visitor)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when the step has a DocString" do
|
56
|
+
let(:location) { double }
|
57
|
+
let(:outline_step) { OutlineStep.new(language, location, keyword, name, doc_string) }
|
58
|
+
let(:doc_string) { DocString.new('a <arg> that needs replacing', '', location) }
|
59
|
+
let(:name) { 'anything' }
|
60
|
+
|
61
|
+
it "replaces the arguments in the DocString" do
|
62
|
+
visitor = double
|
63
|
+
visitor.stub(:step).and_yield
|
64
|
+
expect( visitor ).to receive(:doc_string) do |doc_string|
|
65
|
+
expect( doc_string.content ).to eq "a replacement that needs replacing"
|
66
|
+
end
|
67
|
+
row = ExamplesTable::Row.new({'arg' => 'replacement'}, 1, location)
|
68
|
+
step = outline_step.to_step(row)
|
69
|
+
step.describe_to(visitor)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'cucumber/core/ast/step'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
module Core
|
5
|
+
module Ast
|
6
|
+
describe Step do
|
7
|
+
let(:step) do
|
8
|
+
language, location, keyword, name = double, double, double, double
|
9
|
+
Step.new(language, location, keyword, name)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "describing itself" do
|
13
|
+
let(:visitor) { double }
|
14
|
+
|
15
|
+
it "describes itself as a step" do
|
16
|
+
expect( visitor ).to receive(:step).with(step)
|
17
|
+
step.describe_to(visitor)
|
18
|
+
end
|
19
|
+
|
20
|
+
context "with no multiline argument" do
|
21
|
+
it "does not try to describe any children" do
|
22
|
+
visitor.stub(:step).with(step).and_yield
|
23
|
+
step.describe_to(visitor)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with a multiline argument" do
|
28
|
+
let(:step) { Step.new(double, double, double, double, multiline_arg) }
|
29
|
+
let(:multiline_arg) { double }
|
30
|
+
|
31
|
+
it "tells its multiline argument to describe itself" do
|
32
|
+
visitor.stub(:step).with(step).and_yield
|
33
|
+
expect( multiline_arg ).to receive(:describe_to).with(visitor)
|
34
|
+
step.describe_to(visitor)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'cucumber/core'
|
2
|
+
require 'cucumber/core/compiler'
|
3
|
+
require 'cucumber/core/gherkin/writer'
|
4
|
+
|
5
|
+
module Cucumber::Core
|
6
|
+
describe Compiler do
|
7
|
+
include Gherkin::Writer
|
8
|
+
include Cucumber::Core
|
9
|
+
|
10
|
+
def self.stubs(*names)
|
11
|
+
names.each do |name|
|
12
|
+
let(name) { double(name.to_s) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "compiles a feature with a single scenario" do
|
17
|
+
gherkin_documents = [
|
18
|
+
gherkin do
|
19
|
+
feature do
|
20
|
+
scenario do
|
21
|
+
step 'passing'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
]
|
26
|
+
compile(gherkin_documents) do |visitor|
|
27
|
+
expect( visitor ).to receive(:test_case).once.ordered.and_yield
|
28
|
+
expect( visitor ).to receive(:test_step).once.ordered
|
29
|
+
expect( visitor ).to receive(:done).once.ordered
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "compiles a feature with a background" do
|
34
|
+
gherkin_documents = [
|
35
|
+
gherkin do
|
36
|
+
feature do
|
37
|
+
background do
|
38
|
+
step 'passing'
|
39
|
+
end
|
40
|
+
|
41
|
+
scenario do
|
42
|
+
step 'passing'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
]
|
47
|
+
compile(gherkin_documents) do |visitor|
|
48
|
+
expect( visitor ).to receive(:test_case).once.ordered.and_yield
|
49
|
+
expect( visitor ).to receive(:test_step).exactly(2).times.ordered
|
50
|
+
expect( visitor ).to receive(:done).once.ordered
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "compiles multiple features" do
|
55
|
+
gherkin_documents = [
|
56
|
+
gherkin do
|
57
|
+
feature do
|
58
|
+
background do
|
59
|
+
step 'passing'
|
60
|
+
end
|
61
|
+
scenario do
|
62
|
+
step 'passing'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end,
|
66
|
+
gherkin do
|
67
|
+
feature do
|
68
|
+
background do
|
69
|
+
step 'passing'
|
70
|
+
end
|
71
|
+
scenario do
|
72
|
+
step 'passing'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
]
|
77
|
+
compile(gherkin_documents) do |visitor|
|
78
|
+
expect( visitor ).to receive(:test_case).once.ordered
|
79
|
+
expect( visitor ).to receive(:test_step).twice.ordered
|
80
|
+
expect( visitor ).to receive(:test_case).once.ordered
|
81
|
+
expect( visitor ).to receive(:test_step).twice.ordered
|
82
|
+
expect( visitor ).to receive(:done).once
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "compiling scenario outlines" do
|
87
|
+
it "compiles a scenario outline to test cases" do
|
88
|
+
gherkin_documents = [
|
89
|
+
gherkin do
|
90
|
+
feature do
|
91
|
+
background do
|
92
|
+
step 'passing'
|
93
|
+
end
|
94
|
+
|
95
|
+
scenario_outline do
|
96
|
+
step 'passing <arg>'
|
97
|
+
step 'passing'
|
98
|
+
|
99
|
+
examples 'examples 1' do
|
100
|
+
row 'arg'
|
101
|
+
row '1'
|
102
|
+
row '2'
|
103
|
+
end
|
104
|
+
|
105
|
+
examples 'examples 2' do
|
106
|
+
row 'arg'
|
107
|
+
row 'a'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
]
|
113
|
+
compile(gherkin_documents) do |visitor|
|
114
|
+
expect( visitor ).to receive(:test_case).exactly(3).times.and_yield
|
115
|
+
expect( visitor ).to receive(:test_step).exactly(9).times
|
116
|
+
expect( visitor ).to receive(:done).once
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'replaces arguments correctly when generating test steps' do
|
121
|
+
gherkin_documents = [
|
122
|
+
gherkin do
|
123
|
+
feature do
|
124
|
+
scenario_outline do
|
125
|
+
step 'passing <arg1> with <arg2>'
|
126
|
+
step 'as well as <arg3>'
|
127
|
+
|
128
|
+
examples do
|
129
|
+
row 'arg1', 'arg2', 'arg3'
|
130
|
+
row '1', '2', '3'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
]
|
136
|
+
|
137
|
+
compile(gherkin_documents) do |visitor|
|
138
|
+
expect( visitor ).to receive(:test_step) do |test_step|
|
139
|
+
visit_source(test_step) do |source_visitor|
|
140
|
+
expect( source_visitor ).to receive(:step) do |step|
|
141
|
+
expect(step.name).to eq 'passing 1 with 2'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end.once.ordered
|
145
|
+
|
146
|
+
expect( visitor ).to receive(:test_step) do |test_step|
|
147
|
+
visit_source(test_step) do |source_visitor|
|
148
|
+
expect( source_visitor ).to receive(:step) do |step|
|
149
|
+
expect(step.name).to eq 'as well as 3'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end.once.ordered
|
153
|
+
|
154
|
+
expect( visitor ).to receive(:done).once.ordered
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe Compiler::FeatureCompiler do
|
160
|
+
let(:receiver) { double('receiver') }
|
161
|
+
let(:compiler) { Compiler::FeatureCompiler.new(receiver) }
|
162
|
+
|
163
|
+
context "a scenario with a background" do
|
164
|
+
stubs(:feature,
|
165
|
+
:background,
|
166
|
+
:background_step,
|
167
|
+
:scenario,
|
168
|
+
:scenario_step)
|
169
|
+
|
170
|
+
it "sets the source correctly on the test steps" do
|
171
|
+
expect( receiver ).to receive(:on_background_step).with(
|
172
|
+
[feature, background, background_step]
|
173
|
+
)
|
174
|
+
expect( receiver ).to receive(:on_step).with(
|
175
|
+
[feature, scenario, scenario_step]
|
176
|
+
)
|
177
|
+
expect( receiver ).to receive(:on_test_case).with(
|
178
|
+
[feature, scenario]
|
179
|
+
)
|
180
|
+
compiler.feature(feature) do |f|
|
181
|
+
f.background(background) do |b|
|
182
|
+
b.step background_step
|
183
|
+
end
|
184
|
+
f.scenario(scenario) do |s|
|
185
|
+
s.step scenario_step
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "a scenario outline" do
|
192
|
+
stubs(:feature,
|
193
|
+
:background,
|
194
|
+
:background_step,
|
195
|
+
:scenario_outline,
|
196
|
+
:outline_step,
|
197
|
+
:examples_table_1,
|
198
|
+
:examples_table_1_row_1,
|
199
|
+
:outline_ast_step,
|
200
|
+
:examples_table_2,
|
201
|
+
:examples_table_2_row_1,
|
202
|
+
)
|
203
|
+
|
204
|
+
it "sets the source correctly on the test steps" do
|
205
|
+
outline_step.stub(to_step: outline_ast_step)
|
206
|
+
expect( receiver ).to receive(:on_step).with(
|
207
|
+
[feature, scenario_outline, examples_table_1, examples_table_1_row_1, outline_ast_step]
|
208
|
+
).ordered
|
209
|
+
expect( receiver ).to receive(:on_test_case).with(
|
210
|
+
[feature, scenario_outline, examples_table_1, examples_table_1_row_1]
|
211
|
+
).ordered
|
212
|
+
expect( receiver ).to receive(:on_step).with(
|
213
|
+
[feature, scenario_outline, examples_table_2, examples_table_2_row_1, outline_ast_step]
|
214
|
+
).ordered
|
215
|
+
expect( receiver ).to receive(:on_test_case).with(
|
216
|
+
[feature, scenario_outline, examples_table_2, examples_table_2_row_1]
|
217
|
+
).ordered
|
218
|
+
compiler.feature(feature) do |f|
|
219
|
+
f.scenario_outline(scenario_outline) do |o|
|
220
|
+
o.outline_step outline_step
|
221
|
+
o.examples_table(examples_table_1) do |t|
|
222
|
+
t.examples_table_row(examples_table_1_row_1)
|
223
|
+
end
|
224
|
+
o.examples_table(examples_table_2) do |t|
|
225
|
+
t.examples_table_row(examples_table_2_row_1)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def visit_source(node)
|
234
|
+
visitor = double.as_null_object
|
235
|
+
yield visitor
|
236
|
+
node.describe_source_to(visitor)
|
237
|
+
end
|
238
|
+
|
239
|
+
def compile(gherkin_documents)
|
240
|
+
visitor = double
|
241
|
+
visitor.stub(:test_suite).and_yield
|
242
|
+
visitor.stub(:test_case).and_yield
|
243
|
+
yield visitor
|
244
|
+
super(gherkin_documents, visitor)
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|