Commit 0974d0a5 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge branch 'caalberts-knapsack-file-list' into 'master'

Add custom parallel rspec runner [RUN ALL RSPEC]

See merge request gitlab-org/gitlab!48453
parents 82d2753f 22cc18eb
......@@ -56,7 +56,7 @@ function update_tests_mapping() {
}
function crystalball_rspec_data_exists() {
compgen -G "crystalball/rspec*.yml" > /dev/null;
compgen -G "crystalball/rspec*.yml" >/dev/null
}
function rspec_simple_job() {
......@@ -117,7 +117,13 @@ function rspec_paralellized_job() {
export MEMORY_TEST_PATH="tmp/memory_test/${report_name}_memory.csv"
knapsack rspec "-Ispec -rspec_helper --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts}"
local rspec_args="-Ispec -rspec_helper --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts}"
if [[ -n $RSPEC_MATCHING_TESTS_ENABLED ]]; then
tooling/bin/parallel_rspec --rspec_args "${rspec_args}" --filter tmp/matching_tests.txt
else
tooling/bin/parallel_rspec --rspec_args "${rspec_args}"
fi
date
}
......
# frozen_string_literal: true
require_relative '../../../../tooling/lib/tooling/parallel_rspec_runner'
RSpec.describe Tooling::ParallelRSpecRunner do # rubocop:disable RSpec/FilePath
describe '#run' do
let(:allocator) { instance_double(Knapsack::Allocator) }
let(:rspec_args) { '--seed 123' }
let(:filter_tests_file) { 'tests.txt' }
let(:node_tests) { %w[01_spec.rb 03_spec.rb 05_spec.rb] }
let(:filter_tests) { '01_spec.rb 02_spec.rb 03_spec.rb' }
let(:test_dir) { 'spec' }
before do
allow(Knapsack.logger).to receive(:info)
allow(allocator).to receive(:node_tests).and_return(node_tests)
allow(allocator).to receive(:test_dir).and_return(test_dir)
allow(File).to receive(:exist?).with(filter_tests_file).and_return(true)
allow(File).to receive(:read).and_call_original
allow(File).to receive(:read).with(filter_tests_file).and_return(filter_tests)
allow(subject).to receive(:exec)
end
subject { described_class.new(allocator: allocator, filter_tests_file: filter_tests_file, rspec_args: rspec_args) }
shared_examples 'runs node tests' do
it 'runs rspec with tests allocated for this node' do
expect_command(%w[bundle exec rspec --seed 123 --default-path spec -- 01_spec.rb 03_spec.rb 05_spec.rb])
subject.run
end
end
context 'given filter tests' do
it 'reads filter tests file for list of tests' do
expect(File).to receive(:read).with(filter_tests_file)
subject.run
end
it 'runs rspec filter tests that are allocated for this node' do
expect_command(%w[bundle exec rspec --seed 123 --default-path spec -- 01_spec.rb 03_spec.rb])
subject.run
end
end
context 'with empty filter tests file' do
let(:filter_tests) { '' }
it_behaves_like 'runs node tests'
end
context 'without filter_tests_file option' do
let(:filter_tests_file) { nil }
it_behaves_like 'runs node tests'
end
context 'if filter_tests_file does not exist' do
before do
allow(File).to receive(:exist?).with(filter_tests_file).and_return(false)
end
it_behaves_like 'runs node tests'
end
context 'without rspec args' do
let(:rspec_args) { nil }
it 'runs rspec with without extra arguments' do
expect_command(%w[bundle exec rspec --default-path spec -- 01_spec.rb 03_spec.rb])
subject.run
end
end
def expect_command(cmd)
expect(subject).to receive(:exec).with(*cmd)
end
end
end
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'optparse'
require_relative '../lib/tooling/parallel_rspec_runner'
options = {}
OptionParser.new do |opts|
opts.on("--rspec_args rspec_args", String, "Optional rspec arguments") do |value|
options[:rspec_args] = value
end
opts.on("--filter filter_tests_file", String, "Optional filename containing tests to be filtered") do |value|
options[:filter_tests_file] = value
end
end.parse!
Tooling::ParallelRSpecRunner.run(options)
# frozen_string_literal: true
require 'knapsack'
# 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.
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(allocator: knapsack_allocator, filter_tests_file: nil, rspec_args: nil)
@allocator = allocator
@filter_tests_file = filter_tests_file
@rspec_args = rspec_args&.split(' ') || []
end
def run
Knapsack.logger.info
Knapsack.logger.info 'Knapsack node specs:'
Knapsack.logger.info node_tests
Knapsack.logger.info
Knapsack.logger.info 'Filter specs:'
Knapsack.logger.info filter_tests
Knapsack.logger.info
Knapsack.logger.info 'Running specs:'
Knapsack.logger.info tests_to_run
Knapsack.logger.info
exec(*rspec_command)
end
private
attr_reader :allocator, :filter_tests_file, :rspec_args
def rspec_command
%w[bundle exec rspec].tap do |cmd|
cmd.push(*rspec_args)
cmd.push('--default-path', allocator.test_dir)
cmd.push('--')
cmd.push(*tests_to_run)
end
end
def tests_to_run
return node_tests if filter_tests.empty?
node_tests & filter_tests
end
def node_tests
allocator.node_tests
end
def 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 knapsack_allocator
Knapsack::AllocatorBuilder.new(Knapsack::Adapters::RSpecAdapter).allocator
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment