cucumber 0.8.7 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gitignore +24 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +16 -3
  4. data/Rakefile +4 -50
  5. data/cucumber.gemspec +36 -600
  6. data/features/cucumber_cli.feature +1 -1
  7. data/features/json_formatter.feature +1 -1
  8. data/features/junit_formatter.feature +10 -6
  9. data/features/post_configuration_hook.feature +15 -2
  10. data/features/step_definitions/cucumber_steps.rb +5 -1
  11. data/features/step_definitions/wire_steps.rb +1 -0
  12. data/features/support/env.rb +2 -5
  13. data/features/wire_protocol.feature +1 -1
  14. data/lib/cucumber.rb +8 -0
  15. data/lib/cucumber/ast/outline_table.rb +4 -4
  16. data/lib/cucumber/ast/step_invocation.rb +14 -13
  17. data/lib/cucumber/ast/table.rb +2 -1
  18. data/lib/cucumber/ast/tree_walker.rb +3 -3
  19. data/lib/cucumber/cli/configuration.rb +32 -7
  20. data/lib/cucumber/cli/main.rb +26 -30
  21. data/lib/cucumber/cli/options.rb +1 -3
  22. data/lib/cucumber/cli/profile_loader.rb +2 -0
  23. data/lib/cucumber/configuration.rb +37 -0
  24. data/lib/cucumber/errors.rb +40 -0
  25. data/lib/cucumber/feature_file.rb +5 -12
  26. data/lib/cucumber/formatter/junit.rb +2 -2
  27. data/lib/cucumber/formatter/tag_cloud.rb +1 -1
  28. data/lib/cucumber/js_support/js_dsl.js +4 -4
  29. data/lib/cucumber/js_support/js_language.rb +9 -5
  30. data/lib/cucumber/language_support.rb +2 -2
  31. data/lib/cucumber/parser/gherkin_builder.rb +19 -19
  32. data/lib/cucumber/platform.rb +3 -4
  33. data/lib/cucumber/rake/task.rb +1 -7
  34. data/lib/cucumber/rb_support/rb_dsl.rb +1 -0
  35. data/lib/cucumber/rb_support/rb_language.rb +1 -0
  36. data/lib/cucumber/rspec/doubles.rb +3 -3
  37. data/lib/cucumber/runtime.rb +192 -0
  38. data/lib/cucumber/runtime/features_loader.rb +62 -0
  39. data/lib/cucumber/runtime/results.rb +46 -0
  40. data/lib/cucumber/runtime/support_code.rb +174 -0
  41. data/lib/cucumber/runtime/user_interface.rb +80 -0
  42. data/lib/cucumber/step_mother.rb +6 -427
  43. data/lib/cucumber/wire_support/configuration.rb +2 -0
  44. data/lib/cucumber/wire_support/wire_language.rb +1 -8
  45. data/spec/cucumber/ast/background_spec.rb +3 -3
  46. data/spec/cucumber/ast/feature_spec.rb +2 -2
  47. data/spec/cucumber/ast/scenario_outline_spec.rb +1 -1
  48. data/spec/cucumber/ast/scenario_spec.rb +1 -2
  49. data/spec/cucumber/ast/tree_walker_spec.rb +1 -1
  50. data/spec/cucumber/cli/configuration_spec.rb +31 -5
  51. data/spec/cucumber/cli/drb_client_spec.rb +1 -1
  52. data/spec/cucumber/cli/main_spec.rb +8 -37
  53. data/spec/cucumber/cli/options_spec.rb +20 -0
  54. data/spec/cucumber/formatter/spec_helper.rb +5 -7
  55. data/spec/cucumber/rb_support/rb_language_spec.rb +2 -2
  56. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +1 -1
  57. data/spec/cucumber/runtime_spec.rb +294 -0
  58. data/spec/cucumber/step_match_spec.rb +10 -8
  59. data/spec/cucumber/world/pending_spec.rb +1 -1
  60. data/spec/spec_helper.rb +2 -21
  61. metadata +215 -84
  62. data/Caliper.yml +0 -4
  63. data/VERSION.yml +0 -5
  64. data/spec/cucumber/step_mother_spec.rb +0 -302
@@ -0,0 +1,62 @@
1
+ require 'cucumber/errors'
2
+
3
+ module Cucumber
4
+ class Runtime
5
+
6
+ class FeaturesLoader
7
+ include Formatter::Duration
8
+
9
+ def initialize(feature_files, filters, tag_expression)
10
+ @feature_files, @filters, @tag_expression = feature_files, filters, tag_expression
11
+ end
12
+
13
+ def features
14
+ load unless @features
15
+ @features
16
+ end
17
+
18
+ private
19
+
20
+ def load
21
+ features = Ast::Features.new
22
+
23
+ tag_counts = {}
24
+ start = Time.new
25
+ log.debug("Features:\n")
26
+ @feature_files.each do |f|
27
+ feature_file = FeatureFile.new(f)
28
+ feature = feature_file.parse(@filters, tag_counts)
29
+ if feature
30
+ features.add_feature(feature)
31
+ log.debug(" * #{f}\n")
32
+ end
33
+ end
34
+ duration = Time.now - start
35
+ log.debug("Parsing feature files took #{format_duration(duration)}\n\n")
36
+
37
+ check_tag_limits(tag_counts)
38
+
39
+ @features = features
40
+ end
41
+
42
+ def check_tag_limits(tag_counts)
43
+ error_messages = []
44
+ @tag_expression.limits.each do |tag_name, tag_limit|
45
+ tag_locations = (tag_counts[tag_name] || [])
46
+ tag_count = tag_locations.length
47
+ if tag_count > tag_limit
48
+ error = "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
49
+ tag_locations.join("\n ")
50
+ error_messages << error
51
+ end
52
+ end
53
+ raise TagExcess.new(error_messages) if error_messages.any?
54
+ end
55
+
56
+ def log
57
+ Cucumber.logger
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,46 @@
1
+ module Cucumber
2
+ class Runtime
3
+
4
+ class Results
5
+ def initialize(configuration)
6
+ @configuration = configuration
7
+ end
8
+
9
+ def step_visited(step) #:nodoc:
10
+ steps << step unless steps.index(step)
11
+ end
12
+
13
+ def scenario_visited(scenario) #:nodoc:
14
+ scenarios << scenario unless scenarios.index(scenario)
15
+ end
16
+
17
+ def steps(status = nil) #:nodoc:
18
+ @steps ||= []
19
+ if(status)
20
+ @steps.select{|step| step.status == status}
21
+ else
22
+ @steps
23
+ end
24
+ end
25
+
26
+ def scenarios(status = nil) #:nodoc:
27
+ @scenarios ||= []
28
+ if(status)
29
+ @scenarios.select{|scenario| scenario.status == status}
30
+ else
31
+ @scenarios
32
+ end
33
+ end
34
+
35
+ def failure?
36
+ if @configuration.wip?
37
+ scenarios(:passed).any?
38
+ else
39
+ scenarios(:failed).any? ||
40
+ (@configuration.strict? && (steps(:undefined).any? || steps(:pending).any?))
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,174 @@
1
+ require 'cucumber/constantize'
2
+
3
+ module Cucumber
4
+ class Runtime
5
+
6
+ class SupportCode
7
+ class StepInvoker
8
+ include Gherkin::Rubify
9
+
10
+ def initialize(support_code)
11
+ @support_code = support_code
12
+ end
13
+
14
+ def uri(uri)
15
+ end
16
+
17
+ def step(step)
18
+ cucumber_multiline_arg = case(rubify(step.multiline_arg))
19
+ when Gherkin::Formatter::Model::PyString
20
+ step.multiline_arg.value
21
+ when Array
22
+ Ast::Table.new(step.multiline_arg.map{|row| row.cells})
23
+ else
24
+ nil
25
+ end
26
+ @support_code.invoke(step.name, cucumber_multiline_arg)
27
+ end
28
+
29
+ def eof
30
+ end
31
+ end
32
+
33
+ include Constantize
34
+
35
+ def initialize(step_mother, in_guess_mode)
36
+ @step_mother = step_mother
37
+ @guess_step_matches = in_guess_mode
38
+ @unsupported_programming_languages = []
39
+ @programming_languages = []
40
+ @language_map = {}
41
+ end
42
+
43
+ def invoke_steps(steps_text, i18n, file_colon_line)
44
+ file, line = file_colon_line.split(':')
45
+ parser = Gherkin::Parser::Parser.new(StepInvoker.new(self), true, 'steps')
46
+ parser.parse(steps_text, file, line.to_i)
47
+ end
48
+
49
+ def load_programming_language!(ext)
50
+ return @language_map[ext] if @language_map[ext]
51
+ programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language")
52
+ programming_language = programming_language_class.new(@step_mother)
53
+ @programming_languages << programming_language
54
+ @language_map[ext] = programming_language
55
+ programming_language
56
+ end
57
+
58
+ def load_files!(files)
59
+ log.debug("Code:\n")
60
+ files.each do |file|
61
+ load_file(file)
62
+ end
63
+ log.debug("\n")
64
+ end
65
+
66
+ def unmatched_step_definitions
67
+ @programming_languages.map do |programming_language|
68
+ programming_language.unmatched_step_definitions
69
+ end.flatten
70
+ end
71
+
72
+ def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
73
+ load_programming_language!('rb') if unknown_programming_language?
74
+ @programming_languages.map do |programming_language|
75
+ programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
76
+ end.join("\n")
77
+ end
78
+
79
+ def unknown_programming_language?
80
+ @programming_languages.empty?
81
+ end
82
+
83
+ def fire_hook(name, *args)
84
+ @programming_languages.each do |programming_language|
85
+ programming_language.send(name, *args)
86
+ end
87
+ end
88
+
89
+ def around(scenario, block)
90
+ @programming_languages.reverse.inject(block) do |blk, programming_language|
91
+ proc do
92
+ programming_language.around(scenario) do
93
+ blk.call(scenario)
94
+ end
95
+ end
96
+ end.call
97
+ end
98
+
99
+ def step_match(step_name, name_to_report=nil) #:nodoc:
100
+ matches = matches(step_name, name_to_report)
101
+ raise Undefined.new(step_name) if matches.empty?
102
+ matches = best_matches(step_name, matches) if matches.size > 1 && guess_step_matches?
103
+ raise Ambiguous.new(step_name, matches, guess_step_matches?) if matches.size > 1
104
+ matches[0]
105
+ end
106
+
107
+ def invoke(step_name, multiline_argument=nil)
108
+ begin
109
+ step_match(step_name).invoke(multiline_argument)
110
+ rescue Exception => e
111
+ e.nested! if Undefined === e
112
+ raise e
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def guess_step_matches?
119
+ @guess_step_matches
120
+ end
121
+
122
+ def matches(step_name, name_to_report)
123
+ @programming_languages.map do |programming_language|
124
+ programming_language.step_matches(step_name, name_to_report).to_a
125
+ end.flatten
126
+ end
127
+
128
+ def best_matches(step_name, step_matches) #:nodoc:
129
+ no_groups = step_matches.select {|step_match| step_match.args.length == 0}
130
+ max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
131
+ top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
132
+
133
+ if no_groups.any?
134
+ longest_regexp_length = no_groups.map {|step_match| step_match.text_length }.max
135
+ no_groups.select {|step_match| step_match.text_length == longest_regexp_length }
136
+ elsif top_groups.any?
137
+ shortest_capture_length = top_groups.map {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } }.min
138
+ top_groups.select {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } == shortest_capture_length }
139
+ else
140
+ top_groups
141
+ end
142
+ end
143
+
144
+ def load_file(file)
145
+ if programming_language = programming_language_for(file)
146
+ log.debug(" * #{file}\n")
147
+ programming_language.load_code_file(file)
148
+ else
149
+ log.debug(" * #{file} [NOT SUPPORTED]\n")
150
+ end
151
+ end
152
+
153
+ def log
154
+ Cucumber.logger
155
+ end
156
+
157
+ def programming_language_for(step_def_file) #:nodoc:
158
+ if ext = File.extname(step_def_file)[1..-1]
159
+ return nil if @unsupported_programming_languages.index(ext)
160
+ begin
161
+ load_programming_language!(ext)
162
+ rescue LoadError => e
163
+ log.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n")
164
+ @unsupported_programming_languages << ext
165
+ nil
166
+ end
167
+ else
168
+ nil
169
+ end
170
+ end
171
+
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,80 @@
1
+ require 'timeout'
2
+
3
+ module Cucumber
4
+ class Runtime
5
+
6
+ module UserInterface
7
+ attr_writer :visitor
8
+
9
+ # Output +announcement+ alongside the formatted output.
10
+ # This is an alternative to using Kernel#puts - it will display
11
+ # nicer, and in all outputs (in case you use several formatters)
12
+ #
13
+ def announce(msg)
14
+ msg.respond_to?(:join) ? @visitor.announce(msg.join("\n")) : @visitor.announce(msg.to_s)
15
+ end
16
+
17
+ # Suspends execution and prompts +question+ to the console (STDOUT).
18
+ # An operator (manual tester) can then enter a line of text and hit
19
+ # <ENTER>. The entered text is returned, and both +question+ and
20
+ # the result is added to the output using #announce.
21
+ #
22
+ # If you want a beep to happen (to grab the manual tester's attention),
23
+ # just prepend ASCII character 7 to the question:
24
+ #
25
+ # ask("#{7.chr}How many cukes are in the external system?")
26
+ #
27
+ # If that doesn't issue a beep, you can shell out to something else
28
+ # that makes a sound before invoking #ask.
29
+ #
30
+ def ask(question, timeout_seconds)
31
+ STDOUT.puts(question)
32
+ STDOUT.flush
33
+ announce(question)
34
+
35
+ if(Cucumber::JRUBY)
36
+ answer = jruby_gets(timeout_seconds)
37
+ else
38
+ answer = mri_gets(timeout_seconds)
39
+ end
40
+
41
+ if(answer)
42
+ announce(answer)
43
+ answer
44
+ else
45
+ raise("Waited for input for #{timeout_seconds} seconds, then timed out.")
46
+ end
47
+ end
48
+
49
+ # Embed +file+ of MIME type +mime_type+ into the output. This may or may
50
+ # not be ignored, depending on what kind of formatter(s) are active.
51
+ #
52
+ def embed(file, mime_type)
53
+ @visitor.embed(file, mime_type)
54
+ end
55
+
56
+ private
57
+
58
+ def mri_gets(timeout_seconds)
59
+ begin
60
+ Timeout.timeout(timeout_seconds) do
61
+ STDIN.gets
62
+ end
63
+ rescue Timeout::Error => e
64
+ nil
65
+ end
66
+ end
67
+
68
+ def jruby_gets(timeout_seconds)
69
+ answer = nil
70
+ t = java.lang.Thread.new do
71
+ answer = STDIN.gets
72
+ end
73
+ t.start
74
+ t.join(timeout_seconds * 1000)
75
+ answer
76
+ end
77
+ end
78
+
79
+ end
80
+ end
@@ -1,431 +1,10 @@
1
- require 'cucumber/constantize'
2
- require 'cucumber/core_ext/instance_exec'
3
- require 'cucumber/language_support/language_methods'
4
- require 'cucumber/formatter/duration'
5
- require 'cucumber/cli/options'
6
- require 'timeout'
1
+ require 'cucumber/runtime'
7
2
 
8
3
  module Cucumber
9
- # Raised when there is no matching StepDefinition for a step.
10
- class Undefined < StandardError
11
- attr_reader :step_name
12
-
13
- def initialize(step_name)
14
- super %{Undefined step: "#{step_name}"}
15
- @step_name = step_name
16
- end
17
-
18
- def nested!
19
- @nested = true
20
- end
21
-
22
- def nested?
23
- @nested
24
- end
25
- end
26
-
27
- # Raised when a StepDefinition's block invokes World#pending
28
- class Pending < StandardError
29
- end
30
-
31
- # Raised when a step matches 2 or more StepDefinitions
32
- class Ambiguous < StandardError
33
- def initialize(step_name, step_definitions, used_guess)
34
- message = "Ambiguous match of \"#{step_name}\":\n\n"
35
- message << step_definitions.map{|sd| sd.backtrace_line}.join("\n")
36
- message << "\n\n"
37
- message << "You can run again with --guess to make Cucumber be more smart about it\n" unless used_guess
38
- super(message)
39
- end
40
- end
41
-
42
- class TagExcess < StandardError
43
- def initialize(messages)
44
- super(messages.join("\n"))
45
- end
46
- end
47
-
48
- # This is the meaty part of Cucumber that ties everything together.
49
- class StepMother
50
- include Constantize
51
- include Formatter::Duration
52
- attr_writer :options, :visitor, :log
53
-
54
- def initialize
55
- @unsupported_programming_languages = []
56
- @programming_languages = []
57
- @language_map = {}
58
- @current_scenario = nil
59
- end
60
-
61
- def load_plain_text_features(feature_files)
62
- features = Ast::Features.new
63
-
64
- tag_counts = {}
65
- start = Time.new
66
- log.debug("Features:\n")
67
- feature_files.each do |f|
68
- feature_file = FeatureFile.new(f)
69
- feature = feature_file.parse(options, tag_counts)
70
- if feature
71
- features.add_feature(feature)
72
- log.debug(" * #{f}\n")
73
- end
74
- end
75
- duration = Time.now - start
76
- log.debug("Parsing feature files took #{format_duration(duration)}\n\n")
77
-
78
- check_tag_limits(tag_counts)
79
-
80
- features
81
- end
82
-
83
- def check_tag_limits(tag_counts)
84
- error_messages = []
85
- options[:tag_expression].limits.each do |tag_name, tag_limit|
86
- tag_locations = (tag_counts[tag_name] || [])
87
- tag_count = tag_locations.length
88
- if tag_count > tag_limit
89
- error = "#{tag_name} occurred #{tag_count} times, but the limit was set to #{tag_limit}\n " +
90
- tag_locations.join("\n ")
91
- error_messages << error
92
- end
93
- end
94
- raise TagExcess.new(error_messages) if error_messages.any?
95
- end
96
-
97
- def load_code_files(step_def_files)
98
- log.debug("Code:\n")
99
- step_def_files.each do |step_def_file|
100
- load_code_file(step_def_file)
101
- end
102
- log.debug("\n")
103
- end
104
-
105
- def load_code_file(step_def_file)
106
- if programming_language = programming_language_for(step_def_file)
107
- log.debug(" * #{step_def_file}\n")
108
- programming_language.load_code_file(step_def_file)
109
- else
110
- log.debug(" * #{step_def_file} [NOT SUPPORTED]\n")
111
- end
112
- end
113
-
114
- # Loads and registers programming language implementation.
115
- # Instances are cached, so calling with the same argument
116
- # twice will return the same instance.
117
- #
118
- def load_programming_language(ext)
119
- return @language_map[ext] if @language_map[ext]
120
- programming_language_class = constantize("Cucumber::#{ext.capitalize}Support::#{ext.capitalize}Language")
121
- programming_language = programming_language_class.new(self)
122
- @programming_languages << programming_language
123
- @language_map[ext] = programming_language
124
- programming_language
125
- end
126
-
127
- # Returns the options passed on the command line.
128
- def options
129
- @options ||= Cli::Options.new
130
- end
131
-
132
- def step_visited(step) #:nodoc:
133
- steps << step unless steps.index(step)
134
- end
135
-
136
- def steps(status = nil) #:nodoc:
137
- @steps ||= []
138
- if(status)
139
- @steps.select{|step| step.status == status}
140
- else
141
- @steps
142
- end
143
- end
144
-
145
- # Output +announcement+ alongside the formatted output.
146
- # This is an alternative to using Kernel#puts - it will display
147
- # nicer, and in all outputs (in case you use several formatters)
148
- #
149
- def announce(msg)
150
- msg.respond_to?(:join) ? @visitor.announce(msg.join("\n")) : @visitor.announce(msg.to_s)
151
- end
152
-
153
- # Suspends execution and prompts +question+ to the console (STDOUT).
154
- # An operator (manual tester) can then enter a line of text and hit
155
- # <ENTER>. The entered text is returned, and both +question+ and
156
- # the result is added to the output using #announce.
157
- #
158
- # If you want a beep to happen (to grab the manual tester's attention),
159
- # just prepend ASCII character 7 to the question:
160
- #
161
- # ask("#{7.chr}How many cukes are in the external system?")
162
- #
163
- # If that doesn't issue a beep, you can shell out to something else
164
- # that makes a sound before invoking #ask.
165
- #
166
- def ask(question, timeout_seconds)
167
- STDOUT.puts(question)
168
- STDOUT.flush
169
- announce(question)
170
-
171
- if(Cucumber::JRUBY)
172
- answer = jruby_gets(timeout_seconds)
173
- else
174
- answer = mri_gets(timeout_seconds)
175
- end
176
-
177
- if(answer)
178
- announce(answer)
179
- answer
180
- else
181
- raise("Waited for input for #{timeout_seconds} seconds, then timed out.")
182
- end
183
- end
184
-
185
- # Embed +file+ of MIME type +mime_type+ into the output. This may or may
186
- # not be ignored, depending on what kind of formatter(s) are active.
187
- #
188
- def embed(file, mime_type)
189
- @visitor.embed(file, mime_type)
190
- end
191
-
192
- def scenarios(status = nil) #:nodoc:
193
- @scenarios ||= []
194
- if(status)
195
- @scenarios.select{|scenario| scenario.status == status}
196
- else
197
- @scenarios
198
- end
199
- end
200
-
201
- def invoke(step_name, multiline_argument=nil)
202
- begin
203
- step_match(step_name).invoke(multiline_argument)
204
- rescue Exception => e
205
- e.nested! if Undefined === e
206
- raise e
207
- end
208
- end
209
-
210
- # Invokes a series of steps +steps_text+. Example:
211
- #
212
- # invoke(%Q{
213
- # Given I have 8 cukes in my belly
214
- # Then I should not be thirsty
215
- # })
216
- def invoke_steps(steps_text, i18n, file_colon_line)
217
- file, line = file_colon_line.split(':')
218
- parser = Gherkin::Parser::Parser.new(StepInvoker.new(self), true, 'steps')
219
- parser.parse(steps_text, file, line.to_i)
220
- end
221
-
222
- class StepInvoker
223
- def initialize(step_mother)
224
- @step_mother = step_mother
225
- end
226
-
227
- def step(statement, multiline_arg, result)
228
- cucumber_multiline_arg = case(multiline_arg)
229
- when Gherkin::Formatter::Model::PyString
230
- multiline_arg.value
231
- when Array
232
- Ast::Table.new(multiline_arg.map{|row| row.cells})
233
- else
234
- nil
235
- end
236
- @step_mother.invoke(*[statement.name, cucumber_multiline_arg].compact)
237
- end
238
-
239
- def eof
240
- end
241
- end
242
-
243
- # Returns a Cucumber::Ast::Table for +text_or_table+, which can either
244
- # be a String:
245
- #
246
- # table(%{
247
- # | account | description | amount |
248
- # | INT-100 | Taxi | 114 |
249
- # | CUC-101 | Peeler | 22 |
250
- # })
251
- #
252
- # or a 2D Array:
253
- #
254
- # table([
255
- # %w{ account description amount },
256
- # %w{ INT-100 Taxi 114 },
257
- # %w{ CUC-101 Peeler 22 }
258
- # ])
259
- #
260
- def table(text_or_table, file=nil, line_offset=0)
261
- if Array === text_or_table
262
- Ast::Table.new(text_or_table)
263
- else
264
- Ast::Table.parse(text_or_table, file, line_offset)
265
- end
266
- end
267
-
268
- # Returns a regular String for +string_with_triple_quotes+. Example:
269
- #
270
- # """
271
- # hello
272
- # world
273
- # """
274
- #
275
- # Is retured as: " hello\nworld"
276
- #
277
- def py_string(string_with_triple_quotes, file=nil, line_offset=0)
278
- Ast::PyString.parse(string_with_triple_quotes)
279
- end
280
-
281
- def step_match(step_name, name_to_report=nil) #:nodoc:
282
- matches = @programming_languages.map do |programming_language|
283
- programming_language.step_matches(step_name, name_to_report).to_a
284
- end.flatten
285
- raise Undefined.new(step_name) if matches.empty?
286
- matches = best_matches(step_name, matches) if matches.size > 1 && options[:guess]
287
- raise Ambiguous.new(step_name, matches, options[:guess]) if matches.size > 1
288
- matches[0]
289
- end
290
-
291
- def best_matches(step_name, step_matches) #:nodoc:
292
- no_groups = step_matches.select {|step_match| step_match.args.length == 0}
293
- max_arg_length = step_matches.map {|step_match| step_match.args.length }.max
294
- top_groups = step_matches.select {|step_match| step_match.args.length == max_arg_length }
295
-
296
- if no_groups.any?
297
- longest_regexp_length = no_groups.map {|step_match| step_match.text_length }.max
298
- no_groups.select {|step_match| step_match.text_length == longest_regexp_length }
299
- elsif top_groups.any?
300
- shortest_capture_length = top_groups.map {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } }.min
301
- top_groups.select {|step_match| step_match.args.inject(0) {|sum, c| sum + c.to_s.length } == shortest_capture_length }
302
- else
303
- top_groups
304
- end
305
- end
306
-
307
- def unmatched_step_definitions
308
- @programming_languages.map do |programming_language|
309
- programming_language.unmatched_step_definitions
310
- end.flatten
311
- end
312
-
313
- def snippet_text(step_keyword, step_name, multiline_arg_class) #:nodoc:
314
- load_programming_language('rb') if unknown_programming_language?
315
- @programming_languages.map do |programming_language|
316
- programming_language.snippet_text(step_keyword, step_name, multiline_arg_class)
317
- end.join("\n")
318
- end
319
-
320
- def unknown_programming_language?
321
- @programming_languages.empty?
322
- end
323
-
324
- def with_hooks(scenario, skip_hooks=false)
325
- around(scenario, skip_hooks) do
326
- before_and_after(scenario, skip_hooks) do
327
- yield scenario
328
- end
329
- end
330
- end
331
-
332
- def around(scenario, skip_hooks=false, &block) #:nodoc:
333
- unless skip_hooks
334
- @programming_languages.reverse.inject(block) do |blk, programming_language|
335
- proc do
336
- programming_language.around(scenario) do
337
- blk.call(scenario)
338
- end
339
- end
340
- end.call
341
- else
342
- yield
343
- end
344
- end
345
-
346
- def before_and_after(scenario, skip_hooks=false) #:nodoc:
347
- before(scenario) unless skip_hooks
348
- yield scenario
349
- after(scenario) unless skip_hooks
350
- scenario_visited(scenario)
351
- end
352
-
353
- def before(scenario) #:nodoc:
354
- return if options[:dry_run] || @current_scenario
355
- @current_scenario = scenario
356
- @programming_languages.each do |programming_language|
357
- programming_language.before(scenario)
358
- end
359
- end
360
-
361
- def after(scenario) #:nodoc:
362
- @current_scenario = nil
363
- return if options[:dry_run]
364
- @programming_languages.each do |programming_language|
365
- programming_language.after(scenario)
366
- end
367
- end
368
-
369
- def after_step #:nodoc:
370
- return if options[:dry_run]
371
- @programming_languages.each do |programming_language|
372
- programming_language.execute_after_step(@current_scenario)
373
- end
374
- end
375
-
376
- def after_configuration(configuration) #:nodoc
377
- @programming_languages.each do |programming_language|
378
- programming_language.after_configuration(configuration)
379
- end
380
- end
381
-
382
- private
383
-
384
- def programming_language_for(step_def_file) #:nodoc:
385
- if ext = File.extname(step_def_file)[1..-1]
386
- return nil if @unsupported_programming_languages.index(ext)
387
- begin
388
- load_programming_language(ext)
389
- rescue LoadError => e
390
- log.debug("Failed to load '#{ext}' programming language for file #{step_def_file}: #{e.message}\n")
391
- @unsupported_programming_languages << ext
392
- nil
393
- end
394
- else
395
- nil
396
- end
397
- end
398
-
399
- def max_step_definition_length #:nodoc:
400
- @max_step_definition_length ||= step_definitions.map{|step_definition| step_definition.text_length}.max
401
- end
402
-
403
- def scenario_visited(scenario) #:nodoc:
404
- scenarios << scenario unless scenarios.index(scenario)
405
- end
406
-
407
- def log
408
- @log ||= Logger.new(STDOUT)
409
- end
410
-
411
- def mri_gets(timeout_seconds)
412
- begin
413
- Timeout.timeout(timeout_seconds) do
414
- STDIN.gets
415
- end
416
- rescue Timeout::Error => e
417
- nil
418
- end
419
- end
420
-
421
- def jruby_gets(timeout_seconds)
422
- answer = nil
423
- t = java.lang.Thread.new do
424
- answer = STDIN.gets
425
- end
426
- t.start
427
- t.join(timeout_seconds * 1000)
428
- answer
4
+ class StepMother < Runtime
5
+ def initialize(*args)
6
+ warn("StepMother has been deprecated and will be gently put to sleep before the next release. Please use Runtime instead. #{caller[0]}")
7
+ super
429
8
  end
430
9
  end
431
- end
10
+ end