debian-mirror-gitlab/tooling/lib/tooling/parallel_rspec_runner.rb
2023-07-09 08:55:56 +05:30

134 lines
3.6 KiB
Ruby

# frozen_string_literal: true
require 'knapsack'
require 'fileutils'
module Knapsack
module Distributors
class BaseDistributor
# Refine https://github.com/KnapsackPro/knapsack/blob/v1.21.1/lib/knapsack/distributors/base_distributor.rb
# to take in account the additional filtering we do for predictive jobs.
module BaseDistributorWithTestFiltering
attr_reader :filter_tests
def initialize(args = {})
super
@filter_tests = args[:filter_tests]
end
def all_tests
@all_tests ||= begin
pattern_tests = Dir.glob(test_file_pattern).uniq
if filter_tests.empty?
pattern_tests
else
pattern_tests & filter_tests
end
end.sort
end
end
prepend BaseDistributorWithTestFiltering
end
end
class AllocatorBuilder
# Refine https://github.com/KnapsackPro/knapsack/blob/v1.21.1/lib/knapsack/allocator_builder.rb
# to take in account the additional filtering we do for predictive jobs.
module AllocatorBuilderWithTestFiltering
attr_accessor :filter_tests
def allocator
Knapsack::Allocator.new({
report: Knapsack.report.open,
test_file_pattern: test_file_pattern,
ci_node_total: Knapsack::Config::Env.ci_node_total,
ci_node_index: Knapsack::Config::Env.ci_node_index,
# Additional argument
filter_tests: filter_tests
})
end
end
prepend AllocatorBuilderWithTestFiltering
end
end
# A custom parallel rspec runner based on Knapsack runner
# which takes in additional option for a file containing
# list of test files.
#
# When executing RSpec in CI, the list of tests allocated by Knapsack
# will be compared with the test files listed in the file.
#
# Only the test files allocated by Knapsack and listed in the file
# would be executed in the CI node.
#
# Reference:
# https://github.com/ArturT/knapsack/blob/v1.21.1/lib/knapsack/runners/rspec_runner.rb
module Tooling
class ParallelRSpecRunner
def self.run(rspec_args: nil, filter_tests_file: nil)
new(rspec_args: rspec_args, filter_tests_file: filter_tests_file).run
end
def initialize(filter_tests_file: nil, rspec_args: nil)
@filter_tests_file = filter_tests_file
@rspec_args = rspec_args&.split(' ') || []
end
def run
if ENV['KNAPSACK_RSPEC_SUITE_REPORT_PATH']
knapsack_dir = File.dirname(ENV['KNAPSACK_RSPEC_SUITE_REPORT_PATH'])
FileUtils.mkdir_p(knapsack_dir)
File.write(File.join(knapsack_dir, 'node_specs.txt'), node_tests.join("\n"))
end
if node_tests.empty?
Knapsack.logger.info 'No tests to run on this node, exiting.'
return
end
Knapsack.logger.info "Running command: #{rspec_command.join(' ')}"
exec(*rspec_command)
end
private
attr_reader :filter_tests_file, :rspec_args
def rspec_command
%w[bundle exec rspec].tap do |cmd|
cmd.push(*rspec_args)
cmd.push('--')
cmd.push(*node_tests)
end
end
def node_tests
allocator.node_tests
end
def filter_tests
@filter_tests ||=
filter_tests_file ? tests_from_file(filter_tests_file) : []
end
def tests_from_file(filter_tests_file)
return [] unless File.exist?(filter_tests_file)
File.read(filter_tests_file).split(' ')
end
def allocator
@allocator ||=
Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).tap do |builder|
builder.filter_tests = filter_tests
end.allocator
end
end
end