cucumber 2.0.0.beta.3 → 2.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +20 -3
  3. data/cucumber.gemspec +2 -1
  4. data/examples/tcl/features/step_definitions/fib_steps.rb +1 -1
  5. data/features/docs/extending_cucumber/custom_formatter.feature +65 -7
  6. data/features/docs/formatters/debug_formatter.feature +24 -17
  7. data/features/docs/formatters/pretty_formatter.feature +42 -0
  8. data/features/docs/formatters/rerun_formatter.feature +3 -2
  9. data/lib/cucumber/cli/configuration.rb +3 -7
  10. data/lib/cucumber/cli/main.rb +1 -1
  11. data/lib/cucumber/{runtime → filters}/gated_receiver.rb +5 -1
  12. data/lib/cucumber/filters/quit.rb +24 -0
  13. data/lib/cucumber/filters/randomizer.rb +36 -0
  14. data/lib/cucumber/filters/tag_limits.rb +40 -0
  15. data/lib/cucumber/{runtime → filters}/tag_limits/test_case_index.rb +4 -2
  16. data/lib/cucumber/{runtime → filters}/tag_limits/verifier.rb +4 -2
  17. data/lib/cucumber/formatter/console.rb +2 -2
  18. data/lib/cucumber/formatter/debug.rb +1 -8
  19. data/lib/cucumber/formatter/fanout.rb +27 -0
  20. data/lib/cucumber/formatter/gherkin_formatter_adapter.rb +1 -3
  21. data/lib/cucumber/formatter/html.rb +12 -4
  22. data/lib/cucumber/formatter/ignore_missing_messages.rb +20 -0
  23. data/lib/cucumber/formatter/junit.rb +2 -2
  24. data/lib/cucumber/formatter/legacy_api/adapter.rb +1008 -0
  25. data/lib/cucumber/formatter/legacy_api/ast.rb +374 -0
  26. data/lib/cucumber/formatter/legacy_api/results.rb +51 -0
  27. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +30 -0
  28. data/lib/cucumber/formatter/pretty.rb +4 -0
  29. data/lib/cucumber/formatter/rerun.rb +14 -88
  30. data/lib/cucumber/language_support/language_methods.rb +0 -54
  31. data/lib/cucumber/multiline_argument/data_table.rb +3 -4
  32. data/lib/cucumber/platform.rb +1 -1
  33. data/lib/cucumber/runtime.rb +41 -107
  34. data/spec/cucumber/{runtime → filters}/gated_receiver_spec.rb +3 -3
  35. data/spec/cucumber/{runtime → filters}/tag_limits/test_case_index_spec.rb +3 -3
  36. data/spec/cucumber/{runtime → filters}/tag_limits/verifier_spec.rb +4 -4
  37. data/spec/cucumber/{runtime/tag_limits/filter_spec.rb → filters/tag_limits_spec.rb} +6 -6
  38. data/spec/cucumber/formatter/debug_spec.rb +39 -530
  39. data/spec/cucumber/formatter/html_spec.rb +56 -0
  40. data/spec/cucumber/formatter/legacy_api/adapter_spec.rb +1902 -0
  41. data/spec/cucumber/formatter/pretty_spec.rb +128 -0
  42. data/spec/cucumber/formatter/rerun_spec.rb +106 -0
  43. data/spec/cucumber/formatter/spec_helper.rb +6 -2
  44. data/spec/cucumber/rb_support/rb_language_spec.rb +2 -2
  45. data/spec/cucumber/rb_support/rb_step_definition_spec.rb +1 -1
  46. data/spec/cucumber/runtime_spec.rb +1 -5
  47. data/spec/spec_helper.rb +2 -0
  48. metadata +44 -29
  49. data/features/docs/extending_cucumber/formatter_callbacks.feature +0 -370
  50. data/features/docs/output_from_hooks.feature +0 -128
  51. data/lib/cucumber/reports/legacy_formatter.rb +0 -1349
  52. data/lib/cucumber/runtime/results.rb +0 -64
  53. data/lib/cucumber/runtime/tag_limits.rb +0 -15
  54. data/lib/cucumber/runtime/tag_limits/filter.rb +0 -31
  55. data/spec/cucumber/reports/legacy_formatter_spec.rb +0 -1860
  56. data/spec/cucumber/runtime/results_spec.rb +0 -88
@@ -72,6 +72,7 @@ module Cucumber
72
72
  end
73
73
 
74
74
  def after_feature_element(feature_element)
75
+ print_messages
75
76
  @io.puts
76
77
  @io.flush
77
78
  end
@@ -83,6 +84,7 @@ module Cucumber
83
84
  end
84
85
 
85
86
  def after_background(background)
87
+ print_messages
86
88
  @in_background = nil
87
89
  @io.puts
88
90
  @io.flush
@@ -125,6 +127,7 @@ module Cucumber
125
127
  def before_step(step)
126
128
  @current_step = step
127
129
  @indent = 6
130
+ print_messages
128
131
  end
129
132
 
130
133
  def before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background, file_colon_line)
@@ -161,6 +164,7 @@ module Cucumber
161
164
 
162
165
  def exception(exception, status)
163
166
  return if @hide_this_step
167
+ print_messages
164
168
  print_exception(exception, status, @indent)
165
169
  @io.flush
166
170
  end
@@ -2,106 +2,32 @@ require 'cucumber/formatter/io'
2
2
 
3
3
  module Cucumber
4
4
  module Formatter
5
- # The formatter used for <tt>--format rerun</tt>
6
- #
7
- # This formatter keeps track of all failing features and print out their location.
8
- # Example:
9
- #
10
- # features/foo.feature:34 features/bar.feature:11:76:81
11
- #
12
- # This formatter is used by AutoTest - it will use the output to decide what
13
- # to run the next time, simply passing the output string on the command line.
14
- #
15
5
  class Rerun
16
- include Io
6
+ include Formatter::Io
17
7
 
18
8
  def initialize(runtime, path_or_io, options)
19
9
  @io = ensure_io(path_or_io, "rerun")
20
- @options = options
21
- @file_names = []
22
- @file_colon_lines = Hash.new{|h,k| h[k] = []}
10
+ @failures = {}
23
11
  end
24
12
 
25
- def before_feature(feature_element)
26
- @lines = []
27
- @file = feature_element.file
28
- # See https://github.com/cucumber/cucumber/issues/629
29
- if @file.include?(' ')
30
- warn("Filenames with spaces like '#{@file}' cause unexpected behaviour from the rerun formatter.")
31
- end
13
+ def after_test_case(test_case, result)
14
+ return if result.passed?
15
+ @failures[test_case.location.file] ||= []
16
+ @failures[test_case.location.file] << test_case.location.line
32
17
  end
33
18
 
34
- def after_feature(*)
35
- unless @lines.empty?
36
- after_first_time do
37
- @io.print ' '
38
- end
39
- @io.print "#{@file}:#{@lines.join(':')}"
40
- @io.flush
41
- end
19
+ def done
20
+ return if @failures.empty?
21
+ @io.print file_failures.join(' ')
42
22
  end
43
23
 
44
- def after_features(features)
45
- @io.close
24
+ [:before_test_case, :before_test_step, :after_test_step].each do |method|
25
+ define_method(method) { |*| }
46
26
  end
47
27
 
48
- def before_feature_element(feature_element)
49
- @rerun = false
50
- end
51
-
52
- def after_feature_element(feature_element)
53
- if (@rerun || feature_element.failed? || feature_element.status == :skipped) && !(Cucumber::Reports::Legacy::Ast::ScenarioOutline === feature_element)
54
- @lines << feature_element.line
55
- end
56
- end
57
-
58
- def after_table_row(table_row)
59
- return unless @in_examples and Cucumber::Reports::LegacyExampleTableRow === table_row
60
- unless @header_row
61
- if table_row.failed? || table_row.status == :skipped
62
- @rerun = true
63
- @lines << table_row.line
64
- end
65
- end
66
-
67
- @header_row = false if @header_row
68
- end
69
-
70
- def before_examples(*args)
71
- @header_row = true
72
- @in_examples = true
73
- @current_example_line = nil
74
- end
75
-
76
- def after_examples(*args)
77
- @in_examples = false
78
- if @current_example_line and @rerun
79
- @lines << @current_example_line
80
- end
81
- end
82
-
83
- def before_table_row(table_row)
84
- return unless @in_examples
85
- end
86
-
87
- def step_name(keyword, step_match, status, source_indent, background, file_colon_line)
88
- @rerun = true if [:failed, :pending, :undefined].index(status)
89
- end
90
-
91
- def scenario_name(keyword, name, file_colon_line, source_indent)
92
- return unless @in_examples
93
- if @current_example_line and @rerun
94
- @lines << @current_example_line
95
- end
96
- @rerun = false
97
- @current_example_line = file_colon_line.split(':')[1]
98
- end
99
-
100
- private
101
-
102
- def after_first_time
103
- yield if @not_first_time
104
- @not_first_time = true
28
+ private
29
+ def file_failures
30
+ @failures.map { |file, lines| [file, lines].join(':') }
105
31
  end
106
32
  end
107
33
  end
@@ -4,21 +4,6 @@ require 'cucumber/step_definition_light'
4
4
  module Cucumber
5
5
  module LanguageSupport
6
6
  module LanguageMethods
7
- def around(scenario)
8
- execute_around(scenario) do
9
- yield
10
- end
11
- end
12
-
13
- def before(scenario)
14
- begin_scenario(scenario)
15
- execute_before(scenario)
16
- end
17
-
18
- def after(scenario)
19
- execute_after(scenario)
20
- end_scenario
21
- end
22
7
 
23
8
  def after_configuration(configuration)
24
9
  hooks[:after_configuration].each do |hook|
@@ -26,12 +11,6 @@ module Cucumber
26
11
  end
27
12
  end
28
13
 
29
- def execute_after_step(scenario)
30
- hooks_for(:after_step, scenario).each do |hook|
31
- invoke(hook, 'AfterStep', scenario)
32
- end
33
- end
34
-
35
14
  def execute_transforms(args)
36
15
  args.map do |arg|
37
16
  matching_transform = transforms.detect {|transform| transform.match(arg) }
@@ -87,39 +66,6 @@ module Cucumber
87
66
  @transforms ||= []
88
67
  end
89
68
 
90
- def execute_around(scenario, &block)
91
- hooks_for(:around, scenario).reverse.inject(block) do |blk, hook|
92
- proc do
93
- invoke(hook, 'Around', scenario, true) do
94
- blk.call(scenario)
95
- end
96
- end
97
- end.call
98
- end
99
-
100
- def execute_before(scenario)
101
- hooks_for(:before, scenario).each do |hook|
102
- invoke(hook, 'Before', scenario, true)
103
- end
104
- end
105
-
106
- def execute_after(scenario)
107
- hooks_for(:after, scenario).reverse_each do |hook|
108
- invoke(hook, 'After', scenario, true)
109
- end
110
- end
111
-
112
- def invoke(hook, location, scenario, exception_fails_scenario, &block)
113
- begin
114
- hook.invoke(location, scenario, &block)
115
- rescue Exception => exception
116
- if exception_fails_scenario
117
- scenario.fail!(exception)
118
- else
119
- raise
120
- end
121
- end
122
- end
123
69
  end
124
70
  end
125
71
  end
@@ -427,8 +427,9 @@ module Cucumber
427
427
  end
428
428
 
429
429
  def to_s(options = {}) #:nodoc:
430
+ # TODO: factor out this code so we don't depend on such a big lump of old cruft
430
431
  require 'cucumber/formatter/pretty'
431
- require 'cucumber/reports/legacy_formatter'
432
+ require 'cucumber/formatter/legacy_api/adapter'
432
433
  options = {:color => true, :indent => 2, :prefixes => TO_S_PREFIXES}.merge(options)
433
434
  io = StringIO.new
434
435
 
@@ -436,9 +437,7 @@ module Cucumber
436
437
  Cucumber::Term::ANSIColor.coloring = options[:color]
437
438
  formatter = Formatter::Pretty.new(nil, io, options)
438
439
  formatter.instance_variable_set('@indent', options[:indent])
439
-
440
- Reports::Legacy::Ast::MultilineArg.for(self).accept(Reports::FormatterWrapper.new([formatter]))
441
-
440
+ Formatter::LegacyApi::Ast::MultilineArg.for(self).accept(Formatter::Fanout.new([formatter]))
442
441
  Cucumber::Term::ANSIColor.coloring = c
443
442
  io.rewind
444
443
  s = "\n" + io.read + (" " * (options[:indent] - 2))
@@ -4,7 +4,7 @@ require 'rbconfig'
4
4
 
5
5
  module Cucumber
6
6
  unless defined?(Cucumber::VERSION)
7
- VERSION = '2.0.0.beta.3'
7
+ VERSION = '2.0.0.beta.4'
8
8
  BINARY = File.expand_path(File.dirname(__FILE__) + '/../../bin/cucumber')
9
9
  LIBDIR = File.expand_path(File.dirname(__FILE__) + '/../../lib')
10
10
  JRUBY = defined?(JRUBY_VERSION)
@@ -9,6 +9,10 @@ require 'cucumber/load_path'
9
9
  require 'cucumber/language_support/language_methods'
10
10
  require 'cucumber/formatter/duration'
11
11
  require 'cucumber/file_specs'
12
+ require 'cucumber/filters/quit'
13
+ require 'cucumber/filters/randomizer'
14
+ require 'cucumber/filters/tag_limits'
15
+ require 'cucumber/formatter/fanout'
12
16
 
13
17
  module Cucumber
14
18
  module FixRuby21Bug9285
@@ -35,9 +39,7 @@ module Cucumber
35
39
 
36
40
  require 'cucumber/core'
37
41
  require 'cucumber/runtime/user_interface'
38
- require 'cucumber/runtime/results'
39
42
  require 'cucumber/runtime/support_code'
40
- require 'cucumber/runtime/tag_limits'
41
43
  class Runtime
42
44
  attr_reader :results, :support_code, :configuration
43
45
 
@@ -46,17 +48,15 @@ module Cucumber
46
48
  include Runtime::UserInterface
47
49
 
48
50
  def initialize(configuration = Configuration.default)
49
- @current_scenario = nil
50
51
  @configuration = Configuration.parse(configuration)
51
52
  @support_code = SupportCode.new(self, @configuration)
52
- @results = Results.new(@configuration)
53
+ @results = Formatter::LegacyApi::Results.new
53
54
  end
54
55
 
55
- # Allows you to take an existing runtime and change it's configuration
56
+ # Allows you to take an existing runtime and change its configuration
56
57
  def configure(new_configuration)
57
58
  @configuration = Configuration.parse(new_configuration)
58
59
  @support_code.configure(@configuration)
59
- @results.configure(@configuration)
60
60
  end
61
61
 
62
62
  def load_programming_language(language)
@@ -79,10 +79,6 @@ module Cucumber
79
79
  @configuration.dry_run?
80
80
  end
81
81
 
82
- def step_visited(step) #:nodoc:
83
- @results.step_visited(step)
84
- end
85
-
86
82
  def scenarios(status = nil)
87
83
  @results.scenarios(status)
88
84
  end
@@ -103,55 +99,10 @@ module Cucumber
103
99
  @support_code.snippet_text(::Gherkin::I18n.code_keyword_for(step_keyword), step_name, multiline_arg)
104
100
  end
105
101
 
106
- def with_hooks(scenario, skip_hooks=false)
107
- around(scenario, skip_hooks) do
108
- before_and_after(scenario, skip_hooks) do
109
- yield scenario
110
- end
111
- end
112
- end
113
-
114
- def around(scenario, skip_hooks=false, &block) #:nodoc:
115
- if skip_hooks
116
- yield
117
- return
118
- end
119
-
120
- @support_code.around(scenario, block)
121
- end
122
-
123
- def before_and_after(scenario, skip_hooks=false) #:nodoc:
124
- before(scenario) unless skip_hooks
125
- yield scenario
126
- after(scenario) unless skip_hooks
127
- record_result scenario
128
- end
129
-
130
- def record_result(scenario)
131
- @results.scenario_visited(scenario)
132
- end
133
-
134
102
  def begin_scenario(scenario)
135
103
  @support_code.fire_hook(:begin_scenario, scenario)
136
104
  end
137
105
 
138
- def before(scenario) #:nodoc:
139
- return if dry_run? || @current_scenario
140
- @current_scenario = scenario
141
- @support_code.fire_hook(:before, scenario)
142
- end
143
-
144
- def after(scenario) #:nodoc:
145
- @current_scenario = nil
146
- return if dry_run?
147
- @support_code.fire_hook(:after, scenario)
148
- end
149
-
150
- def after_step #:nodoc:
151
- return if dry_run?
152
- @support_code.fire_hook(:execute_after_step, @current_scenario)
153
- end
154
-
155
106
  def unknown_programming_language?
156
107
  @support_code.unknown_programming_language?
157
108
  end
@@ -159,8 +110,7 @@ module Cucumber
159
110
  # Returns Ast::DocString for +string_without_triple_quotes+.
160
111
  #
161
112
  def doc_string(string_without_triple_quotes, content_type='', line_offset=0)
162
- file, line = *caller[0].split(':')[0..1]
163
- location = Core::Ast::Location.new(file, line)
113
+ location = Core::Ast::Location.of_caller
164
114
  Core::Ast::DocString.new(string_without_triple_quotes, content_type, location)
165
115
  end
166
116
 
@@ -228,68 +178,52 @@ module Cucumber
228
178
  @mappings = Mappings.for(self)
229
179
  end
230
180
 
231
- require 'cucumber/reports/legacy_formatter'
181
+ require 'cucumber/formatter/legacy_api/adapter'
182
+ require 'cucumber/formatter/legacy_api/runtime_facade'
183
+ require 'cucumber/formatter/legacy_api/results'
184
+ require 'cucumber/formatter/ignore_missing_messages'
185
+ require 'cucumber/core/report/summary'
232
186
  def report
233
- @report ||= Cucumber::Reports::LegacyFormatter.new(self, @configuration.formatters(self))
187
+ @report ||= Formatter::Fanout.new([summary_report] + formatters)
188
+ end
189
+
190
+ def summary_report
191
+ @summary_report ||= Core::Report::Summary.new
192
+ end
193
+
194
+ def formatters
195
+ @formatters ||= @configuration.formatter_factories { |factory, path_or_io, options|
196
+ results = Formatter::LegacyApi::Results.new
197
+ runtime_facade = Formatter::LegacyApi::RuntimeFacade.new(results, @support_code, @configuration)
198
+ formatter = factory.new(runtime_facade, path_or_io, options)
199
+ Formatter::LegacyApi::Adapter.new(
200
+ Formatter::IgnoreMissingMessages.new(formatter),
201
+ results, @support_code, @configuration)
202
+ }
234
203
  end
235
204
 
205
+ def failure?
206
+ if @configuration.wip?
207
+ summary_report.test_cases.total_passed > 0
208
+ else
209
+ summary_report.test_cases.total_failed > 0 || summary_report.test_steps.total_failed > 0 ||
210
+ (@configuration.strict? && (summary_report.test_steps.total_undefined > 0 || summary_report.test_steps.total_skipped > 0))
211
+ end
212
+ end
213
+ public :failure?
214
+
236
215
  require 'cucumber/core/test/filters'
237
216
  def filters
238
217
  tag_expressions = @configuration.tag_expressions
239
218
  name_regexps = @configuration.name_regexps
240
219
  tag_limits = @configuration.tag_limits
241
220
  [].tap do |filters|
242
- filters << [Cucumber::Runtime::Randomizer, [@configuration.seed]] if @configuration.randomize?
243
- filters << [Cucumber::Runtime::TagLimits::Filter, [tag_limits]] if tag_limits.any?
221
+ filters << [Filters::Randomizer, [@configuration.seed]] if @configuration.randomize?
222
+ filters << [Filters::TagLimits, [tag_limits]] if tag_limits.any?
244
223
  filters << [Cucumber::Core::Test::TagFilter, [tag_expressions]]
245
224
  filters << [Cucumber::Core::Test::NameFilter, [name_regexps]]
246
225
  filters << [Cucumber::Core::Test::LocationsFilter, [filespecs.locations]]
247
- filters << [Quit, []]
248
- end
249
- end
250
-
251
- class Randomizer
252
- def initialize(seed, receiver)
253
- @receiver = receiver
254
- @test_cases = []
255
- @seed = seed
256
- end
257
-
258
- def test_case(test_case)
259
- @test_cases << test_case
260
- end
261
-
262
- def done
263
- shuffled_test_cases.each do |test_case|
264
- test_case.describe_to(@receiver)
265
- end
266
- @receiver.done
267
- end
268
-
269
- private
270
-
271
- def shuffled_test_cases
272
- @test_cases.shuffle(random: Random.new(seed))
273
- end
274
-
275
- attr_reader :seed
276
- private :seed
277
- end
278
-
279
- class Quit
280
- def initialize(receiver)
281
- @receiver = receiver
282
- end
283
-
284
- def test_case(test_case)
285
- unless Cucumber.wants_to_quit
286
- test_case.describe_to @receiver
287
- end
288
- end
289
-
290
- def done
291
- @receiver.done
292
- self
226
+ filters << [Filters::Quit, []]
293
227
  end
294
228
  end
295
229