parallel_tests 2.21.3 → 4.2.1

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.
@@ -1,64 +1,62 @@
1
- require 'cucumber/core/gherkin/tag_expression'
1
+ # frozen_string_literal: true
2
+ require 'cucumber/tag_expressions/parser'
2
3
  require 'cucumber/runtime'
3
4
  require 'cucumber'
4
5
  require 'parallel_tests/cucumber/scenario_line_logger'
5
6
  require 'parallel_tests/gherkin/listener'
6
- require 'gherkin/errors'
7
+
8
+ begin
9
+ gem "cuke_modeler", "~> 3.0"
10
+ require 'cuke_modeler'
11
+ rescue LoadError
12
+ raise 'Grouping by individual cucumber scenarios requires the `cuke_modeler` modeler gem with requirement `~> 3.0`. Add `gem "cuke_modeler", "~> 3.0"` to your `Gemfile`, run `bundle install` and try again.'
13
+ end
7
14
 
8
15
  module ParallelTests
9
16
  module Cucumber
10
17
  class Scenarios
11
18
  class << self
12
- def all(files, options={})
19
+ def all(files, options = {})
20
+ # Parse tag expression from given test options and ignore tag pattern. Refer here to understand how new tag expression syntax works - https://github.com/cucumber/cucumber/tree/master/tag-expressions
13
21
  tags = []
14
- tags.concat options[:ignore_tag_pattern].to_s.split(/\s*,\s*/).map {|tag| "~#{tag}" }
15
- tags.concat options[:test_options].to_s.scan(/(?:-t|--tags) (~?@[\w,~@]+)/).flatten
22
+ words = options[:test_options] || []
23
+ words.each_with_index { |w, i| tags << words[i + 1] if ["-t", "--tags"].include?(w) }
24
+ if ignore = options[:ignore_tag_pattern]
25
+ tags << "not (#{ignore})"
26
+ end
27
+ tags_exp = tags.compact.join(" and ")
16
28
 
17
- split_into_scenarios files, tags.uniq
29
+ split_into_scenarios files, tags_exp
18
30
  end
19
31
 
20
32
  private
21
33
 
22
- def split_into_scenarios(files, tags=[])
23
-
24
- # Create the tag expression instance from gherkin, this is needed to know if the scenario matches with the tags invoked by the request
25
- tag_expression = ::Cucumber::Core::Gherkin::TagExpression.new(tags)
26
-
34
+ def split_into_scenarios(files, tags = '')
35
+ # Create the tag expression instance from cucumber tag expressions parser, this is needed to know if the scenario matches with the tags invoked by the request
27
36
  # Create the ScenarioLineLogger which will filter the scenario we want
28
- scenario_line_logger = ParallelTests::Cucumber::Formatters::ScenarioLineLogger.new(tag_expression)
37
+ args = []
38
+ args << ::Cucumber::TagExpressions::Parser.new.parse(tags) unless tags.empty?
39
+ scenario_line_logger = ParallelTests::Cucumber::Formatters::ScenarioLineLogger.new(*args)
29
40
 
30
41
  # here we loop on the files map, each file will contain one or more scenario
31
- features ||= files.map do |path|
42
+ files.each do |path|
32
43
  # Gather up any line numbers attached to the file path
33
44
  path, *test_lines = path.split(/:(?=\d+)/)
34
45
  test_lines.map!(&:to_i)
35
46
 
36
- # We encode the file and get the content of it
37
- source = ::Cucumber::Runtime::NormalisedEncodingFile.read(path)
38
47
  # We create a Gherkin document, this will be used to decode the details of each scenario
39
- document = ::Cucumber::Core::Gherkin::Document.new(path, source)
40
-
41
- # We create a parser for the gherkin document
42
- parser = ::Gherkin::Parser.new()
43
- scanner = ::Gherkin::TokenScanner.new(document.body)
44
-
45
- begin
46
- # We make an attempt to parse the gherkin document, this could be failed if the document is not well formatted
47
- result = parser.parse(scanner)
48
- feature_tags = result[:feature][:tags].map { |tag| ::Cucumber::Core::Ast::Tag.new(tag[:location], tag[:name]) }
49
-
50
- # We loop on each children of the feature
51
- result[:feature][:children].each do |feature_element|
52
- # If the type of the child is not a scenario or scenario outline, we continue, we are only interested by the name of the scenario here
53
- next unless /Scenario/.match(feature_element[:type])
48
+ document = ::CukeModeler::FeatureFile.new(path)
49
+ feature = document.feature
54
50
 
55
- # It's a scenario, we add it to the scenario_line_logger
56
- scenario_line_logger.visit_feature_element(document.uri, feature_element, feature_tags, line_numbers: test_lines)
57
- end
51
+ # We make an attempt to parse the gherkin document, this could be failed if the document is not well formatted
52
+ feature_tags = feature.tags.map(&:name)
58
53
 
59
- rescue StandardError => e
60
- # Exception if the document is no well formated or error in the tags
61
- raise ::Cucumber::Core::Gherkin::ParseError.new("#{document.uri}: #{e.message}")
54
+ # We loop on each children of the feature
55
+ test_models = feature.tests
56
+ test_models += feature.rules.flat_map(&:tests) if feature.respond_to?(:rules) # cuke_modeler >= 3.2 supports rules
57
+ test_models.each do |test|
58
+ # It's a scenario, we add it to the scenario_line_logger
59
+ scenario_line_logger.visit_feature_element(document.path, test, feature_tags, line_numbers: test_lines)
62
60
  end
63
61
  end
64
62
 
@@ -1,9 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests'
2
3
 
3
4
  module ParallelTests
4
5
  module Gherkin
5
6
  module Io
6
-
7
7
  def prepare_io(path_or_io)
8
8
  if path_or_io.respond_to?(:write)
9
9
  path_or_io
@@ -24,7 +24,7 @@ module ParallelTests
24
24
 
25
25
  # do not let multiple processes get in each others way
26
26
  def lock_output
27
- if File === @io
27
+ if @io.is_a?(File)
28
28
  begin
29
29
  @io.flock File::LOCK_EX
30
30
  yield
@@ -35,7 +35,6 @@ module ParallelTests
35
35
  yield
36
36
  end
37
37
  end
38
-
39
38
  end
40
39
  end
41
40
  end
@@ -1,5 +1,4 @@
1
- require 'gherkin/parser'
2
-
1
+ # frozen_string_literal: true
3
2
  module ParallelTests
4
3
  module Gherkin
5
4
  class Listener
@@ -8,8 +7,10 @@ module ParallelTests
8
7
  attr_writer :ignore_tag_pattern
9
8
 
10
9
  def initialize
11
- @steps, @uris = [], []
10
+ @steps = []
11
+ @uris = []
12
12
  @collect = {}
13
+ @feature, @ignore_tag_pattern = nil
13
14
  reset_counters!
14
15
  end
15
16
 
@@ -17,7 +18,7 @@ module ParallelTests
17
18
  @feature = feature
18
19
  end
19
20
 
20
- def background(*args)
21
+ def background(*)
21
22
  @background = 1
22
23
  end
23
24
 
@@ -32,7 +33,7 @@ module ParallelTests
32
33
  @outline = 1
33
34
  end
34
35
 
35
- def step(*args)
36
+ def step(*)
36
37
  return if @ignoring
37
38
  if @background == 1
38
39
  @background_steps += 1
@@ -52,12 +53,10 @@ module ParallelTests
52
53
  # @param [Gherkin::Formatter::Model::Examples] examples
53
54
  #
54
55
  def examples(examples)
55
- if examples.rows.size > 0
56
- @collect[@uri] += (@outline_steps * examples.rows.size)
57
- end
56
+ @collect[@uri] += (@outline_steps * examples.rows.size) unless examples.rows.empty?
58
57
  end
59
58
 
60
- def eof(*args)
59
+ def eof(*)
61
60
  @collect[@uri] += (@background_steps * @scenarios)
62
61
  reset_counters!
63
62
  end
@@ -68,8 +67,7 @@ module ParallelTests
68
67
  end
69
68
 
70
69
  # ignore lots of other possible callbacks ...
71
- def method_missing(*args)
72
- end
70
+ def method_missing(*); end # rubocop:disable Style/MissingRespondToMissing
73
71
 
74
72
  private
75
73
 
@@ -80,7 +78,7 @@ module ParallelTests
80
78
 
81
79
  # Set @ignoring if we should ignore this scenario/outline based on its tags
82
80
  def should_ignore(scenario)
83
- @ignoring = @ignore_tag_pattern && all_tags(scenario).find{ |tag| @ignore_tag_pattern === tag.name }
81
+ @ignoring = @ignore_tag_pattern && all_tags(scenario).find { |tag| @ignore_tag_pattern === tag.name }
84
82
  end
85
83
  end
86
84
  end
@@ -1,30 +1,27 @@
1
+ # frozen_string_literal: true
1
2
  require "parallel_tests/test/runner"
2
- require 'shellwords'
3
3
 
4
4
  module ParallelTests
5
5
  module Gherkin
6
6
  class Runner < ParallelTests::Test::Runner
7
-
8
7
  class << self
9
8
  def run_tests(test_files, process_number, num_processes, options)
10
9
  combined_scenarios = test_files
11
10
 
12
11
  if options[:group_by] == :scenarios
13
12
  grouped = test_files.map { |t| t.split(':') }.group_by(&:first)
14
- combined_scenarios = grouped.map {|file,files_and_lines| "#{file}:#{files_and_lines.map(&:last).join(':')}" }
13
+ combined_scenarios = grouped.map do |file, files_and_lines|
14
+ "#{file}:#{files_and_lines.map(&:last).join(':')}"
15
+ end
15
16
  end
16
17
 
17
- sanitized_test_files = combined_scenarios.map { |val| WINDOWS ? "\"#{val}\"" : Shellwords.escape(val) }
18
-
19
18
  options[:env] ||= {}
20
- options[:env] = options[:env].merge({'AUTOTEST' => '1'}) if $stdout.tty? # display color when we are in a terminal
21
-
22
- cmd = [
23
- executable,
24
- (runtime_logging if File.directory?(File.dirname(runtime_log))),
25
- cucumber_opts(options[:test_options]),
26
- *sanitized_test_files
27
- ].compact.reject(&:empty?).join(' ')
19
+ options[:env] = options[:env].merge({ 'AUTOTEST' => '1' }) if $stdout.tty?
20
+
21
+ cmd = executable
22
+ cmd += runtime_logging if File.directory?(File.dirname(runtime_log))
23
+ cmd += combined_scenarios
24
+ cmd += cucumber_opts(options[:test_options])
28
25
  execute_command(cmd, process_number, num_processes, options)
29
26
  end
30
27
 
@@ -32,6 +29,10 @@ module ParallelTests
32
29
  @test_file_name || 'feature'
33
30
  end
34
31
 
32
+ def default_test_folder
33
+ 'features'
34
+ end
35
+
35
36
  def test_suffix
36
37
  /\.feature$/
37
38
  end
@@ -44,42 +45,38 @@ module ParallelTests
44
45
  # 1 scenario (1 failed)
45
46
  # 1 step (1 failed)
46
47
  def summarize_results(results)
47
- sort_order = %w[scenario step failed flaky undefined skipped pending passed]
48
+ sort_order = ['scenario', 'step', 'failed', 'flaky', 'undefined', 'skipped', 'pending', 'passed']
48
49
 
49
- %w[scenario step].map do |group|
50
+ ['scenario', 'step'].map do |group|
50
51
  group_results = results.grep(/^\d+ #{group}/)
51
52
  next if group_results.empty?
52
53
 
53
54
  sums = sum_up_results(group_results)
54
55
  sums = sums.sort_by { |word, _| sort_order.index(word) || 999 }
55
56
  sums.map! do |word, number|
56
- plural = "s" if word == group and number != 1
57
+ plural = "s" if (word == group) && (number != 1)
57
58
  "#{number} #{word}#{plural}"
58
59
  end
59
- "#{sums[0]} (#{sums[1..-1].join(", ")})"
60
+ "#{sums[0]} (#{sums[1..].join(", ")})"
60
61
  end.compact.join("\n")
61
62
  end
62
63
 
63
64
  def cucumber_opts(given)
64
- if given =~ /--profile/ or given =~ /(^|\s)-p /
65
+ if given&.include?('--profile') || given&.include?('-p')
65
66
  given
66
67
  else
67
- [given, profile_from_config].compact.join(" ")
68
+ [*given, *profile_from_config]
68
69
  end
69
70
  end
70
71
 
71
72
  def profile_from_config
72
73
  # copied from https://github.com/cucumber/cucumber/blob/master/lib/cucumber/cli/profile_loader.rb#L85
73
74
  config = Dir.glob("{,.config/,config/}#{name}{.yml,.yaml}").first
74
- if config && File.read(config) =~ /^parallel:/
75
- "--profile parallel"
76
- end
75
+ ['--profile', 'parallel'] if config && File.read(config) =~ /^parallel:/
77
76
  end
78
77
 
79
- def tests_in_groups(tests, num_groups, options={})
80
- if options[:group_by] == :scenarios
81
- @test_file_name = "scenario"
82
- end
78
+ def tests_in_groups(tests, num_groups, options = {})
79
+ @test_file_name = "scenario" if options[:group_by] == :scenarios
83
80
  method = "by_#{options[:group_by]}"
84
81
  if Grouper.respond_to?(method)
85
82
  Grouper.send(method, find_tests(tests, options), num_groups, options)
@@ -88,9 +85,8 @@ module ParallelTests
88
85
  end
89
86
  end
90
87
 
91
-
92
88
  def runtime_logging
93
- "--format ParallelTests::Gherkin::RuntimeLogger --out #{runtime_log}"
89
+ ['--format', 'ParallelTests::Gherkin::RuntimeLogger', '--out', runtime_log]
94
90
  end
95
91
 
96
92
  def runtime_log
@@ -98,18 +94,16 @@ module ParallelTests
98
94
  end
99
95
 
100
96
  def determine_executable
101
- case
102
- when File.exist?("bin/#{name}")
97
+ if File.exist?("bin/#{name}")
103
98
  ParallelTests.with_ruby_binary("bin/#{name}")
104
- when ParallelTests.bundler_enabled?
105
- "bundle exec #{name}"
106
- when File.file?("script/#{name}")
99
+ elsif ParallelTests.bundler_enabled?
100
+ ["bundle", "exec", name]
101
+ elsif File.file?("script/#{name}")
107
102
  ParallelTests.with_ruby_binary("script/#{name}")
108
103
  else
109
- "#{name}"
104
+ [name.to_s]
110
105
  end
111
106
  end
112
-
113
107
  end
114
108
  end
115
109
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests/gherkin/io'
2
3
 
3
4
  module ParallelTests
@@ -5,22 +6,22 @@ module ParallelTests
5
6
  class RuntimeLogger
6
7
  include Io
7
8
 
8
- def initialize(step_mother, path_or_io, options)
9
- @io = prepare_io(path_or_io)
9
+ def initialize(config)
10
+ @io = prepare_io(config.out_stream)
10
11
  @example_times = Hash.new(0)
11
- end
12
12
 
13
- def before_feature(_)
14
- @start_at = ParallelTests.now.to_f
15
- end
13
+ config.on_event :test_case_started do |_|
14
+ @start_at = ParallelTests.now.to_f
15
+ end
16
16
 
17
- def after_feature(feature)
18
- @example_times[feature.file] += ParallelTests.now.to_f - @start_at
19
- end
17
+ config.on_event :test_case_finished do |event|
18
+ @example_times[event.test_case.location.file] += ParallelTests.now.to_f - @start_at
19
+ end
20
20
 
21
- def after_features(*args)
22
- lock_output do
23
- @io.puts @example_times.map { |file, time| "#{file}:#{time}" }
21
+ config.on_event :test_run_finished do |_|
22
+ lock_output do
23
+ @io.puts(@example_times.map { |file, time| "#{file}:#{time}" })
24
+ end
24
25
  end
25
26
  end
26
27
  end
@@ -1,35 +1,109 @@
1
+ # frozen_string_literal: true
1
2
  module ParallelTests
2
3
  class Grouper
3
4
  class << self
4
5
  def by_steps(tests, num_groups, options)
5
- features_with_steps = build_features_with_steps(tests, options)
6
+ features_with_steps = group_by_features_with_steps(tests, options)
6
7
  in_even_groups_by_size(features_with_steps, num_groups)
7
8
  end
8
9
 
9
- def by_scenarios(tests, num_groups, options={})
10
+ def by_scenarios(tests, num_groups, options = {})
10
11
  scenarios = group_by_scenarios(tests, options)
11
12
  in_even_groups_by_size(scenarios, num_groups)
12
13
  end
13
14
 
14
- def in_even_groups_by_size(items, num_groups, options= {})
15
- groups = Array.new(num_groups) { {:items => [], :size => 0} }
15
+ def in_even_groups_by_size(items, num_groups, options = {})
16
+ groups = Array.new(num_groups) { { items: [], size: 0 } }
17
+
18
+ return specify_groups(items, num_groups, options, groups) if options[:specify_groups]
16
19
 
17
20
  # add all files that should run in a single process to one group
18
- (options[:single_process] || []).each do |pattern|
19
- matched, items = items.partition { |item, _size| item =~ pattern }
20
- matched.each { |item, size| add_to_group(groups.first, item, size) }
21
+ single_process_patterns = options[:single_process] || []
22
+
23
+ single_items, items = items.partition do |item, _size|
24
+ single_process_patterns.any? { |pattern| item =~ pattern }
25
+ end
26
+
27
+ isolate_count = isolate_count(options)
28
+
29
+ if isolate_count >= num_groups
30
+ raise 'Number of isolated processes must be >= total number of processes'
21
31
  end
22
32
 
23
- groups_to_fill = (options[:isolate] ? groups[1..-1] : groups)
24
- group_features_by_size(items_to_group(items), groups_to_fill)
33
+ if isolate_count >= 1
34
+ # add all files that should run in a multiple isolated processes to their own groups
35
+ group_features_by_size(items_to_group(single_items), groups[0..(isolate_count - 1)])
36
+ # group the non-isolated by size
37
+ group_features_by_size(items_to_group(items), groups[isolate_count..])
38
+ else
39
+ # add all files that should run in a single non-isolated process to first group
40
+ single_items.each { |item, size| add_to_group(groups.first, item, size) }
41
+
42
+ # group all by size
43
+ group_features_by_size(items_to_group(items), groups)
44
+ end
25
45
 
26
46
  groups.map! { |g| g[:items].sort }
27
47
  end
28
48
 
29
49
  private
30
50
 
51
+ def specify_groups(items, num_groups, options, groups)
52
+ specify_test_process_groups = options[:specify_groups].split('|')
53
+ if specify_test_process_groups.count > num_groups
54
+ raise 'Number of processes separated by pipe must be less than or equal to the total number of processes'
55
+ end
56
+
57
+ all_specified_tests = specify_test_process_groups.map { |group| group.split(',') }.flatten
58
+ specified_items_found, items = items.partition { |item, _size| all_specified_tests.include?(item) }
59
+
60
+ specified_specs_not_found = all_specified_tests - specified_items_found.map(&:first)
61
+ if specified_specs_not_found.any?
62
+ raise "Could not find #{specified_specs_not_found} from --specify-groups in the selected files & folders"
63
+ end
64
+
65
+ if specify_test_process_groups.count == num_groups && items.flatten.any?
66
+ raise(
67
+ <<~ERROR
68
+ The number of groups in --specify-groups matches the number of groups from -n but there were other specs
69
+ found in the selected files & folders not specified in --specify-groups. Make sure -n is larger than the
70
+ number of processes in --specify-groups if there are other specs that need to be run. The specs that aren't run:
71
+ #{items.map(&:first)}
72
+ ERROR
73
+ )
74
+ end
75
+
76
+ # First order the specify_groups into the main groups array
77
+ specify_test_process_groups.each_with_index do |specify_test_process, i|
78
+ groups[i] = specify_test_process.split(',')
79
+ end
80
+
81
+ # Return early when processed specify_groups tests exactly match the items passed in
82
+ return groups if specify_test_process_groups.count == num_groups
83
+
84
+ # Now sort the rest of the items into the main groups array
85
+ specified_range = specify_test_process_groups.count..-1
86
+ remaining_groups = groups[specified_range]
87
+ group_features_by_size(items_to_group(items), remaining_groups)
88
+ # Don't sort all the groups, only sort the ones not specified in specify_groups
89
+ sorted_groups = remaining_groups.map { |g| g[:items].sort }
90
+ groups[specified_range] = sorted_groups
91
+
92
+ groups
93
+ end
94
+
95
+ def isolate_count(options)
96
+ if options[:isolate_count] && options[:isolate_count] > 1
97
+ options[:isolate_count]
98
+ elsif options[:isolate]
99
+ 1
100
+ else
101
+ 0
102
+ end
103
+ end
104
+
31
105
  def largest_first(files)
32
- files.sort_by{|_item, size| size }.reverse
106
+ files.sort_by { |_item, size| size }.reverse
33
107
  end
34
108
 
35
109
  def smallest_group(groups)
@@ -41,26 +115,12 @@ module ParallelTests
41
115
  group[:size] += size
42
116
  end
43
117
 
44
- def build_features_with_steps(tests, options)
45
- require 'gherkin/parser'
46
- ignore_tag_pattern = options[:ignore_tag_pattern].nil? ? nil : Regexp.compile(options[:ignore_tag_pattern])
47
- parser = ::Gherkin::Parser.new
48
- # format of hash will be FILENAME => NUM_STEPS
49
- steps_per_file = tests.each_with_object({}) do |file,steps|
50
- feature = parser.parse(File.read(file)).fetch(:feature)
51
-
52
- # skip feature if it matches tag regex
53
- next if feature[:tags].grep(ignore_tag_pattern).any?
54
-
55
- # count the number of steps in the file
56
- # will only include a feature if the regex does not match
57
- all_steps = feature[:children].map{|a| a[:steps].count if a[:tags].grep(ignore_tag_pattern).empty? }.compact
58
- steps[file] = all_steps.inject(0,:+)
59
- end
60
- steps_per_file.sort_by { |_, value| -value }
118
+ def group_by_features_with_steps(tests, options)
119
+ require 'parallel_tests/cucumber/features_with_steps'
120
+ ParallelTests::Cucumber::FeaturesWithSteps.all(tests, options)
61
121
  end
62
122
 
63
- def group_by_scenarios(tests, options={})
123
+ def group_by_scenarios(tests, options = {})
64
124
  require 'parallel_tests/cucumber/scenarios'
65
125
  ParallelTests::Cucumber::Scenarios.all(tests, options)
66
126
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  require 'json'
2
3
 
3
4
  module ParallelTests
4
5
  class Pids
5
- attr_reader :pids, :file_path, :mutex
6
+ attr_reader :file_path, :mutex
6
7
 
7
8
  def initialize(file_path)
8
9
  @file_path = file_path
@@ -42,18 +43,18 @@ module ParallelTests
42
43
 
43
44
  def read
44
45
  sync do
45
- contents = IO.read(file_path)
46
+ contents = File.read(file_path)
46
47
  return if contents.empty?
47
48
  @pids = JSON.parse(contents)
48
49
  end
49
50
  end
50
51
 
51
52
  def save
52
- sync { IO.write(file_path, pids.to_json) }
53
+ sync { File.write(file_path, pids.to_json) }
53
54
  end
54
55
 
55
- def sync
56
- mutex.synchronize { yield }
56
+ def sync(&block)
57
+ mutex.synchronize(&block)
57
58
  end
58
59
  end
59
60
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # rake tasks for Rails 3+
2
3
  module ParallelTests
3
4
  class Railtie < ::Rails::Railtie
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'parallel_tests/rspec/logger_base'
2
3
  require 'parallel_tests/rspec/runner'
3
4
 
4
5
  class ParallelTests::RSpec::FailuresLogger < ParallelTests::RSpec::LoggerBase
5
6
  if RSPEC_2
6
- def dump_failures(*args)
7
- end
7
+ def dump_failures(*args); end
8
8
  else
9
9
  RSpec::Core::Formatters.register self, :dump_summary
10
10
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module ParallelTests
2
3
  module RSpec
3
4
  end
@@ -13,26 +14,27 @@ class ParallelTests::RSpec::LoggerBase < RSpec::Core::Formatters::BaseTextFormat
13
14
 
14
15
  @output ||= args[0]
15
16
 
16
- if String === @output # a path ?
17
+ case @output
18
+ when String # a path ?
17
19
  FileUtils.mkdir_p(File.dirname(@output))
18
- File.open(@output, 'w'){} # overwrite previous results
20
+ File.open(@output, 'w') {} # overwrite previous results
19
21
  @output = File.open(@output, 'a')
20
- elsif File === @output # close and restart in append mode
22
+ when File # close and restart in append mode
21
23
  @output.close
22
24
  @output = File.open(@output.path, 'a')
23
25
  end
24
26
  end
25
27
 
26
- #stolen from Rspec
27
- def close(*args)
28
- @output.close if (IO === @output) & (@output != $stdout)
28
+ # stolen from Rspec
29
+ def close(*)
30
+ @output.close if (IO === @output) & (@output != $stdout)
29
31
  end
30
32
 
31
33
  protected
32
34
 
33
35
  # do not let multiple processes get in each others way
34
36
  def lock_output
35
- if File === @output
37
+ if @output.is_a?(File)
36
38
  begin
37
39
  @output.flock File::LOCK_EX
38
40
  yield