kosmas58-cucumber 0.3.93.1 → 0.3.94

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,42 +6,49 @@ Feature: JUnit output formatter
6
6
  Given I am in junit
7
7
  And the tmp directory is empty
8
8
 
9
- @mri186 @diffxml
9
+ @mri186
10
10
  Scenario: one feature, one passing scenario, one failing scenario
11
11
  When I run cucumber --format junit --out tmp/ features/one_passing_one_failing.feature
12
12
  Then it should fail with
13
13
  """
14
14
 
15
15
  """
16
- And "examples/junit/tmp/TEST-one_passing_one_failing.xml" should contain XML
16
+ And "examples/junit/tmp/TEST-one_passing_one_failing.xml" with junit duration "0.005" should contain
17
17
  """
18
18
  <?xml version="1.0" encoding="UTF-8"?>
19
- <testsuite errors="0" tests="2" name="One passing scenario, one failing scenario" failures="1">
20
- <testcase name="Given a passing scenario" classname="One passing scenario, one failing scenario.Passing">
19
+ <testsuite errors="0" failures="1" name="One passing scenario, one failing scenario" tests="2" time="0.005">
20
+ <testcase classname="One passing scenario, one failing scenario.Passing" name="Passing" time="0.005">
21
21
  </testcase>
22
- <testcase name="Given a failing scenario" classname="One passing scenario, one failing scenario.Failing">
23
- <failure message="Given a failing scenario">
22
+ <testcase classname="One passing scenario, one failing scenario.Failing" name="Failing" time="0.005">
23
+ <failure message="failed Failing" type="failed">
24
+ Scenario: Failing
25
+
26
+ Given a failing scenario
27
+
28
+ Message:
24
29
  (RuntimeError)
25
30
  ./features/step_definitions/steps.rb:6:in `/a failing scenario/'
26
31
  features/one_passing_one_failing.feature:7:in `Given a failing scenario' </failure>
27
32
  </testcase>
28
33
  </testsuite>
29
-
34
+
30
35
  """
31
36
 
32
- @mri186 @diffxml
37
+ @mri186
33
38
  Scenario: pending step
34
39
  When I run cucumber --format junit --out tmp/ features/pending.feature
35
40
  Then it should pass with
36
41
  """
37
42
 
38
43
  """
39
- And "examples/junit/tmp/TEST-pending.xml" should contain XML
44
+ And "examples/junit/tmp/TEST-pending.xml" with junit duration "0.009" should contain
40
45
  """
41
46
  <?xml version="1.0" encoding="UTF-8"?>
42
- <testsuite errors="0" tests="1" name="Pending step" failures="1">
43
- <testcase name="Given a pending step" classname="Pending step.Pending">
44
- <failure message="Given a pending step">
47
+ <testsuite errors="0" failures="1" name="Pending step" tests="1" time="0.009">
48
+ <testcase classname="Pending step.Pending" name="Pending" time="0.009">
49
+ <failure message="pending Pending" type="pending">
50
+ Scenario: Pending
51
+
45
52
  TODO (Cucumber::Pending)
46
53
  ./features/step_definitions/steps.rb:10:in `/a pending step/'
47
54
  features/pending.feature:4:in `Given a pending step' </failure>
@@ -39,7 +39,6 @@ Given /^I am not running (?:.*) in the background$/ do
39
39
  # no-op
40
40
  end
41
41
 
42
-
43
42
  When /^I run cucumber (.*)$/ do |cucumber_opts|
44
43
  run "#{Cucumber::RUBY_BINARY} #{Cucumber::BINARY} --no-color #{cucumber_opts}"
45
44
  end
@@ -75,23 +74,17 @@ Then /^the output should be$/ do |text|
75
74
  last_stdout.should == text
76
75
  end
77
76
 
78
- Then /^"(.*)" should contain XML$/ do |file, xml|
79
- t = Tempfile.new('cucumber-junit')
80
- t.write(xml)
81
- t.flush
82
- t.close
83
- cmd = "diffxml #{t.path} #{file}"
84
- diff = `#{cmd}`
85
- if diff =~ /<delta>/m
86
- raise diff + "\nXML WAS:\n" + IO.read(file)
87
- end
77
+ Then /^"([^\"]*)" should contain$/ do |file, text|
78
+ strip_duration(IO.read(file)).should == text
88
79
  end
89
80
 
90
- Then /^"(.*)" should contain$/ do |file, text|
91
- strip_duration(IO.read(file)).should == text
81
+ Then /^"([^\"]*)" with junit duration "([^\"]*)" should contain$/ do |actual_file, duration_replacement, text|
82
+ actual = IO.read(actual_file)
83
+ actual = replace_junit_duration(actual, duration_replacement)
84
+ actual.should == text
92
85
  end
93
86
 
94
- Then /^"(.*)" should match$/ do |file, text|
87
+ Then /^"([^\"]*)" should match$/ do |file, text|
95
88
  IO.read(file).should =~ Regexp.new(text)
96
89
  end
97
90
 
@@ -119,7 +112,7 @@ Then /^STDERR should be empty$/ do
119
112
  last_stderr.should == ""
120
113
  end
121
114
 
122
- Then /^"(.*)" should exist$/ do |file|
115
+ Then /^"([^\"]*)" should exist$/ do |file|
123
116
  File.exists?(file).should be_true
124
117
  FileUtils.rm(file)
125
118
  end
@@ -50,6 +50,10 @@ class CucumberWorld
50
50
  s.gsub(/\d+m\d+\.\d+s/m, replacement)
51
51
  end
52
52
 
53
+ def replace_junit_duration(s, replacement)
54
+ s.gsub(/\d+\.\d\d+/m, replacement)
55
+ end
56
+
53
57
  def create_file(file_name, file_content)
54
58
  file_content.gsub!("CUCUMBER_LIB", "'#{cucumber_lib_dir}'") # Some files, such as Rakefiles need to use the lib dir
55
59
  in_current_dir do
@@ -118,8 +122,3 @@ end
118
122
  After do
119
123
  terminate_background_jobs
120
124
  end
121
-
122
- Before('@diffxml') do
123
- `diffxml --version`
124
- raise "Please install diffxml from http://diffxml.sourceforge.net/" if $? != 0
125
- end
@@ -70,6 +70,8 @@ Feature: Cucumber --work-in-progress switch
70
70
  1 scenario (1 failed)
71
71
  1 step (1 failed)
72
72
 
73
+ The --wip switch was used, so the failures were expected. All is good.
74
+
73
75
  """
74
76
 
75
77
  Scenario: Pass with Undefined Scenarios
@@ -85,6 +87,8 @@ Feature: Cucumber --work-in-progress switch
85
87
  1 scenario (1 undefined)
86
88
  1 step (1 undefined)
87
89
 
90
+ The --wip switch was used, so the failures were expected. All is good.
91
+
88
92
  """
89
93
 
90
94
  @mri186
@@ -104,6 +108,8 @@ Feature: Cucumber --work-in-progress switch
104
108
  1 scenario (1 pending)
105
109
  1 step (1 pending)
106
110
 
111
+ The --wip switch was used, so the failures were expected. All is good.
112
+
107
113
  """
108
114
 
109
115
  Scenario: Fail with Passing Scenarios
@@ -44,6 +44,19 @@ module Cucumber
44
44
  "#{@file}:#{line}"
45
45
  end
46
46
 
47
+ def tag_count(tag)
48
+ if @tags.respond_to?(:count)
49
+ @tags.count(tag) # 1.9
50
+ else
51
+ @tags.select{|t| t == tag}.length # 1.8
52
+ end
53
+ end
54
+
55
+ def feature_and_children_tag_count(tag)
56
+ children_tag_count = @feature_elements.inject(0){|count, feature_element| count += feature_element.tag_count(tag)}
57
+ children_tag_count + tag_count(tag)
58
+ end
59
+
47
60
  def short_name
48
61
  first_line = name.split(/\n/)[0]
49
62
  if first_line =~ /#{language.keywords('feature', true)}:(.*)/
@@ -24,7 +24,7 @@ module Cucumber
24
24
  if @name.empty?
25
25
  [@keyword.jlength]
26
26
  else
27
- @name.split("\n").enum_for(:each_with_index).map do |line, line_number|
27
+ @name.split("\n").enum_for(:each_with_index).map do |line, line_number|
28
28
  line_number == 0 ? @keyword.jlength + line.jlength : line.jlength + Ast::Step::INDENT - 1 # We -1 as names which are not keyword lines are missing a space between keyword and name
29
29
  end
30
30
  end
@@ -32,7 +32,7 @@ module Cucumber
32
32
 
33
33
  def matches_scenario_names?(scenario_name_regexps)
34
34
  scenario_name_regexps.detect{|name| name =~ @name}
35
- end
35
+ end
36
36
 
37
37
  def backtrace_line(name = "#{@keyword} #{@name}", line = @line)
38
38
  @feature.backtrace_line(name, line) if @feature
@@ -49,9 +49,13 @@ module Cucumber
49
49
  def accept_hook?(hook)
50
50
  @tags.accept_hook?(hook) || @feature.accept_hook?(hook)
51
51
  end
52
-
52
+
53
+ def tag_count(tag)
54
+ @feature.tag_count(tag) == 0 ? @tags.count(tag) : @feature.tag_count(tag)
55
+ end
56
+
53
57
  def language
54
58
  @feature.language
55
59
  end
56
- end
60
+ end
57
61
  end
@@ -30,6 +30,11 @@ module Cucumber
30
30
  end
31
31
  @duration = Time.now - start
32
32
  end
33
+
34
+ def tag_count(tag)
35
+ @features.inject(0){|count, feature| count += feature.feature_and_children_tag_count(tag)}
36
+ end
37
+
33
38
  end
34
39
  end
35
- end
40
+ end
@@ -126,23 +126,30 @@ module Cucumber
126
126
  [:table, *cells_rows.map{|row| row.to_sexp}]
127
127
  end
128
128
 
129
- # Redefines the table headers. This makes it
130
- # possible to use prettier header names in the features. Example:
129
+ # Redefines the table headers. This makes it possible to use
130
+ # prettier and more flexible header names in the features. The
131
+ # keys of +mappings+ are Strings or regular expressions
132
+ # (anything that responds to #=== will work) that may match
133
+ # column headings in the table. The values of +mappings+ are
134
+ # desired names for the columns.
135
+ #
136
+ # Example:
131
137
  #
132
138
  # | Phone Number | Address |
133
139
  # | 123456 | xyz |
134
140
  # | 345678 | abc |
135
141
  #
136
- # A StepDefinition receiving this table can then map the columns:
142
+ # A StepDefinition receiving this table can then map the columns
143
+ # with both Regexp and String:
137
144
  #
138
- # table.map_headers!('Phone Number' => :phone, 'Address' => :address)
145
+ # table.map_headers!(/phone( number)?/i => :phone, 'Address' => :address)
139
146
  # table.hashes
140
147
  # # => [{:phone => '123456', :address => 'xyz'}, {:phone => '345678', :address => 'abc'}]
141
148
  #
142
149
  def map_headers!(mappings)
143
150
  header_cells = cell_matrix[0]
144
151
  mappings.each_pair do |pre, post|
145
- header_cell = header_cells.detect{|cell| cell.value == pre}
152
+ header_cell = header_cells.detect{|cell| pre === cell.value}
146
153
  header_cell.value = post
147
154
  if @conversion_procs.has_key?(pre)
148
155
  @conversion_procs[post] = @conversion_procs.delete(pre)
@@ -10,7 +10,7 @@ module Cucumber
10
10
  def self.strip_prefix(tag_name)
11
11
  tag_name =~ /^@(.*)/ ? $1 : tag_name
12
12
  end
13
-
13
+
14
14
  def initialize(line, tag_names)
15
15
  @line, @tag_names = line, tag_names
16
16
  end
@@ -26,6 +26,14 @@ module Cucumber
26
26
  hook.matches_tag_names?(@tag_names)
27
27
  end
28
28
 
29
+ def count(tag)
30
+ if @tag_names.respond_to?(:count)
31
+ @tag_names.count(tag) # 1.9
32
+ else
33
+ @tag_names.select{|t| t == tag}.length # 1.8
34
+ end
35
+ end
36
+
29
37
  def to_sexp
30
38
  @tag_names.map{|tag_name| [:tag, tag_name]}
31
39
  end
@@ -168,7 +168,7 @@ module Cucumber
168
168
  tr("-", "_").
169
169
  downcase
170
170
  end
171
-
171
+
172
172
  end
173
173
 
174
174
  end
@@ -11,6 +11,8 @@ require 'cucumber/cli/drb_client'
11
11
  module Cucumber
12
12
  module Cli
13
13
  class Main
14
+ FAILURE = 1
15
+
14
16
  class << self
15
17
  def step_mother
16
18
  @step_mother
@@ -32,7 +34,7 @@ module Cucumber
32
34
  @out_stream = out_stream == STDOUT ? Formatter::ColorIO.new : out_stream
33
35
  @error_stream = error_stream
34
36
  end
35
-
37
+
36
38
  def execute!(step_mother)
37
39
  trap_interrupt
38
40
  if configuration.drb?
@@ -56,17 +58,32 @@ module Cucumber
56
58
  step_mother.visitor = visitor # Needed to support World#announce
57
59
  visitor.visit_features(features)
58
60
 
59
- failure = if configuration.wip?
60
- step_mother.scenarios(:passed).any?
61
- else
62
- step_mother.scenarios(:failed).any? ||
63
- (configuration.strict? && step_mother.steps(:undefined).any?)
64
- end
61
+ failure = if exceeded_tag_limts?(features)
62
+ FAILURE
63
+ elsif configuration.wip?
64
+ step_mother.scenarios(:passed).any?
65
+ else
66
+ step_mother.scenarios(:failed).any? ||
67
+ (configuration.strict? && step_mother.steps(:undefined).any?)
68
+ end
65
69
  rescue ProfilesNotDefinedError, YmlLoadError, ProfileNotFound => e
66
70
  @error_stream.puts e.message
67
71
  true
68
72
  end
69
73
 
74
+ def exceeded_tag_limts?(features)
75
+ exceeded = false
76
+ configuration.options[:include_tags].each do |tag, limit|
77
+ unless limit.nil?
78
+ tag_count = features.tag_count(tag)
79
+ if tag_count > limit.to_i
80
+ exceeded = true
81
+ end
82
+ end
83
+ end
84
+ exceeded
85
+ end
86
+
70
87
  def load_plain_text_features
71
88
  features = Ast::Features.new
72
89
 
@@ -85,7 +102,7 @@ module Cucumber
85
102
 
86
103
  def configuration
87
104
  return @configuration if @configuration
88
-
105
+
89
106
  @configuration = Configuration.new(@out_stream, @error_stream)
90
107
  @configuration.parse!(@args)
91
108
  @configuration
@@ -43,6 +43,7 @@ module Cucumber
43
43
  @default_profile = options[:default_profile]
44
44
  @skip_profile_information = options[:skip_profile_information]
45
45
  @profiles = []
46
+ @overridden_paths = []
46
47
  @options = default_options
47
48
  end
48
49
 
@@ -66,7 +67,7 @@ module Cucumber
66
67
  previous_flag_was_profile = true
67
68
  next true
68
69
  end
69
- arg == DRB_FLAG
70
+ arg == DRB_FLAG || @overridden_paths.include?(arg)
70
71
  end
71
72
  )
72
73
  end
@@ -122,12 +123,16 @@ module Cucumber
122
123
  end
123
124
  opts.on("-t TAGS", "--tags TAGS",
124
125
  "Only execute the features or scenarios with the specified tags.",
125
- "TAGS must be comma-separated without spaces. Prefix tags with ~ to",
126
- "exclude features or scenarios having that tag. Tags can be specified",
127
- "with or without the @ prefix.") do |v|
126
+ "TAGS must be comma-separated without spaces. They can be",
127
+ "specified with or without the @ prefix. Example: --tags dev\n",
128
+ "Negative tags: Prefix tags with ~ to exclude features or scenarios",
129
+ "having that tag.\n",
130
+ "Limit WIP: Positive tags can be given a threshold to limit the",
131
+ "number of occurrences. Example: --tags qa:3 will fail if there",
132
+ "are more than 3 occurrences of the @qa tag.") do |v|
128
133
  include_tags, exclude_tags = *parse_tags(v)
129
- @options[:include_tags] += include_tags
130
- @options[:exclude_tags] += exclude_tags
134
+ @options[:include_tags].merge!(include_tags)
135
+ @options[:exclude_tags].merge!(exclude_tags)
131
136
  end
132
137
  opts.on("-n NAME", "--name NAME",
133
138
  "Only execute the feature elements which match part of the given name.",
@@ -225,7 +230,6 @@ module Cucumber
225
230
  @options[:source] = true if @options[:source].nil?
226
231
  end
227
232
 
228
-
229
233
  extract_environment_variables
230
234
  @options[:paths] = @args.dup #whatver is left over
231
235
 
@@ -235,8 +239,6 @@ module Cucumber
235
239
  self
236
240
  end
237
241
 
238
-
239
-
240
242
  protected
241
243
 
242
244
  attr_reader :options, :profiles, :expanded_args
@@ -265,7 +267,16 @@ module Cucumber
265
267
  # Strip @
266
268
  includes = includes.map{|tag| Ast::Tags.strip_prefix(tag)}
267
269
  excludes = excludes.map{|tag| Ast::Tags.strip_prefix(tag)}
268
- [includes, excludes]
270
+ [parse_tag_limits(includes), parse_tag_limits(excludes)]
271
+ end
272
+
273
+ def parse_tag_limits(includes)
274
+ dict = {}
275
+ includes.each do |tag|
276
+ tag, limit = tag.split(':')
277
+ dict[tag] = limit.nil? ? limit : limit.to_i
278
+ end
279
+ dict
269
280
  end
270
281
 
271
282
  def disable_profile_loading?
@@ -302,10 +313,14 @@ module Cucumber
302
313
  def reverse_merge(other_options)
303
314
  @options = other_options.options.merge(@options)
304
315
  @options[:require] += other_options[:require]
305
- @options[:include_tags] += other_options[:include_tags]
306
- @options[:exclude_tags] += other_options[:exclude_tags]
316
+ @options[:include_tags].merge! other_options[:include_tags]
317
+ @options[:exclude_tags].merge! other_options[:exclude_tags]
307
318
  @options[:env_vars] = other_options[:env_vars].merge(@options[:env_vars])
308
- @options[:paths] = other_options[:paths] if @options[:paths].empty?
319
+ if @options[:paths].empty?
320
+ @options[:paths] = other_options[:paths]
321
+ else
322
+ @overridden_paths += (other_options[:paths] - @options[:paths])
323
+ end
309
324
  @options[:source] &= other_options[:source]
310
325
  @options[:snippets] &= other_options[:snippets]
311
326
 
@@ -336,12 +351,12 @@ module Cucumber
336
351
  end
337
352
 
338
353
  def print_profile_information
339
- return if @skip_profile_information || @profiles.empty?
340
- profiles_sentence = ''
341
- profiles_sentence = @profiles.size == 1 ? @profiles.first :
342
- "#{@profiles[0...-1].join(', ')} and #{@profiles.last}"
354
+ return if @skip_profile_information || @profiles.empty?
355
+ profiles_sentence = ''
356
+ profiles_sentence = @profiles.size == 1 ? @profiles.first :
357
+ "#{@profiles[0...-1].join(', ')} and #{@profiles.last}"
343
358
 
344
- @out_stream.puts "Using the #{profiles_sentence} profile#{'s' if @profiles.size> 1}..."
359
+ @out_stream.puts "Using the #{profiles_sentence} profile#{'s' if @profiles.size> 1}..."
345
360
  end
346
361
 
347
362
  def default_options
@@ -352,8 +367,8 @@ module Cucumber
352
367
  :dry_run => false,
353
368
  :formats => [],
354
369
  :excludes => [],
355
- :include_tags => [],
356
- :exclude_tags => [],
370
+ :include_tags => {},
371
+ :exclude_tags => {},
357
372
  :name_regexps => [],
358
373
  :env_vars => {},
359
374
  :diff_enabled => true
@@ -57,8 +57,6 @@ Defined profiles in cucumber.yml:
57
57
  return @cucumber_yml
58
58
  end
59
59
 
60
-
61
-
62
60
  end
63
61
  end
64
62
  end
@@ -2,8 +2,9 @@ module Cucumber
2
2
  class Filter
3
3
  def initialize(lines, options)
4
4
  @lines = lines
5
- @include_tags = options[:include_tags] || []
6
- @exclude_tags = options[:exclude_tags] || []
5
+
6
+ @include_tags = options[:include_tags] ? options[:include_tags].keys : []
7
+ @exclude_tags = options[:exclude_tags] ? options[:exclude_tags].keys : []
7
8
  @name_regexps = options[:name_regexps] || []
8
9
  end
9
10
 
@@ -14,7 +15,7 @@ module Cucumber
14
15
  end
15
16
 
16
17
  def accept_example?(syntax_node, outline)
17
- (at_line?(syntax_node) || outline_at_line?(outline)) &&
18
+ (at_line?(syntax_node) || outline_at_line?(outline)) &&
18
19
  (matches_names?(syntax_node) || outline_matches_names?(outline))
19
20
  end
20
21
 
@@ -60,15 +60,6 @@ module Cucumber
60
60
  module ANSIColor
61
61
  include Term::ANSIColor
62
62
 
63
- # Not supported in Term::ANSIColor
64
- def grey(m)
65
- if ::Term::ANSIColor.coloring?
66
- "\e[90m#{m}\e[0m"
67
- else
68
- m
69
- end
70
- end
71
-
72
63
  ALIASES = Hash.new do |h,k|
73
64
  if k.to_s =~ /(.*)_param/
74
65
  h[$1] + ',bold'
@@ -105,6 +96,47 @@ module Cucumber
105
96
  eval(code)
106
97
  end
107
98
  end
99
+
100
+ def self.define_grey
101
+ begin
102
+ gem 'genki-ruby-terminfo'
103
+ require 'terminfo'
104
+ case TermInfo.default_object.tigetnum("colors")
105
+ when 0
106
+ raise "Your terminal doesn't support colours"
107
+ when 1
108
+ ::Term::ANSIColor.coloring = false
109
+ alias grey white
110
+ when 2..8
111
+ alias grey white
112
+ else
113
+ define_real_grey
114
+ end
115
+ rescue Exception => e
116
+ if e.class.name == 'TermInfo::TermInfoError'
117
+ STDERR.puts "*** WARNING ***"
118
+ STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
119
+ STDERR.puts "Try setting it to TERM=xterm-256color to get grey colour in output"
120
+ STDERR.puts "\n"
121
+ alias grey white
122
+ else
123
+ define_real_grey
124
+ end
125
+ end
126
+ end
127
+
128
+ def self.define_real_grey
129
+ def grey(m)
130
+ if ::Term::ANSIColor.coloring?
131
+ "\e[90m#{m}\e[0m"
132
+ else
133
+ m
134
+ end
135
+ end
136
+ end
137
+
138
+ define_grey
139
+
108
140
  end
109
141
  end
110
142
  end
@@ -6,7 +6,7 @@ module Cucumber
6
6
  module Console
7
7
  extend ANSIColor
8
8
  include Duration
9
-
9
+
10
10
  FORMATS = Hash.new{|hash, format| hash[format] = method(format).to_proc}
11
11
 
12
12
  def format_step(keyword, step_match, status, source_indent)
@@ -59,9 +59,9 @@ module Cucumber
59
59
  end
60
60
 
61
61
  def print_stats(features)
62
-
62
+
63
63
  @failures = step_mother.scenarios(:failed).select { |s| s.is_a?(Cucumber::Ast::Scenario) }
64
-
64
+
65
65
  if !@failures.empty?
66
66
  @io.puts format_string("Failing Scenarios:", :failed)
67
67
  @failures.each do |failure|
@@ -70,7 +70,7 @@ module Cucumber
70
70
  end
71
71
  @io.puts
72
72
  end
73
-
73
+
74
74
  @io.print dump_count(step_mother.scenarios.length, "scenario")
75
75
  print_status_counts{|status| step_mother.scenarios(status)}
76
76
 
@@ -81,7 +81,7 @@ module Cucumber
81
81
 
82
82
  @io.flush
83
83
  end
84
-
84
+
85
85
  def print_exception(e, status, indent)
86
86
  @io.puts(format_string("#{e.message} (#{e.class})\n#{e.backtrace.join("\n")}".indent(indent), status))
87
87
  end
@@ -109,8 +109,33 @@ module Cucumber
109
109
  return unless options[:wip]
110
110
  passed = step_mother.scenarios(:passed)
111
111
  if passed.any?
112
- @io.puts "\nThe --wip switch was used, so I didn't expect anything to pass. These scenarios passed:"
112
+ @io.puts format_string("\nThe --wip switch was used, so I didn't expect anything to pass. These scenarios passed:", :failed)
113
113
  print_elements(passed, :passed, "scenarios")
114
+ else
115
+ @io.puts format_string("\nThe --wip switch was used, so the failures were expected. All is good.\n", :passed)
116
+ end
117
+ end
118
+
119
+ def print_tag_limit_warnings(options)
120
+ return unless tag_limit_breached?(options, @tag_occurences)
121
+ @io.puts
122
+ @io.puts format_string("Failed due to exceeding the tag limit", :failed)
123
+ options[:include_tags].each do |tag_name, limit|
124
+ tag_frequnecy = @tag_occurences[tag_name].size
125
+ if limit && tag_frequnecy > limit
126
+ @io.puts format_string("@#{tag_name} occurred:#{tag_frequnecy} limit:#{limit}", :failed)
127
+ @tag_occurences[tag_name].each {|location| @io.puts format_string(" #{location}", :failed)}
128
+ @io.flush
129
+ end
130
+ end
131
+ end
132
+
133
+ def record_tag_occurrences(feature_element, options)
134
+ @tag_occurences ||= Hash.new{|k,v| k[v] = []}
135
+ options[:include_tags].each do |tag_name, limit|
136
+ if feature_element.tag_count(tag_name) > 0
137
+ @tag_occurences[tag_name] << feature_element.file_colon_line
138
+ end
114
139
  end
115
140
  end
116
141
 
@@ -144,6 +169,15 @@ module Cucumber
144
169
  raise "No format for #{key.inspect}: #{FORMATS.inspect}" if fmt.nil?
145
170
  fmt
146
171
  end
172
+
173
+ def tag_limit_breached?(options, tag_occurences)
174
+ return if tag_occurences.nil?
175
+ tag_limit_breached = false
176
+ options[:include_tags].each do |tag_name, limit|
177
+ tag_limit_breached ||= limit && (tag_occurences[tag_name].size > limit)
178
+ end
179
+ tag_limit_breached
180
+ end
147
181
  end
148
182
  end
149
- end
183
+ end