Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -443,19 +443,19 @@ workflows:
matrix:
parameters:
ruby: ["3.2", "3.3", "3.4"]
rspec: ["3.12.3", "3.13.3"]
rspec: ["3.12.3", "3.13.6"]
- e2e-regular-rspec:
name: e2e-regular__ruby-<< matrix.ruby >>__rspec-<< matrix.rspec >>
matrix:
parameters:
ruby: ["3.2", "3.3", "3.4"]
rspec: ["3.12.3", "3.13.3"]
rspec: ["3.12.3", "3.13.6"]
- e2e-queue-rspec:
name: e2e-queue__ruby-<< matrix.ruby >>__rspec-<< matrix.rspec >>
matrix:
parameters:
ruby: ["3.2", "3.3", "3.4"]
rspec: ["3.12.3", "3.13.3"]
rspec: ["3.12.3", "3.13.6"]
- e2e-regular-minitest:
name: e2e-regular__ruby-<< matrix.ruby >>__minitest
matrix:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### UNRELEASED (minor)

* Allow to [precalculate RSpec Split by Test Examples](https://docs.knapsackpro.com/ruby/reference/#knapsack_pro_rspec_split_by_test_examples_file-rspec)
* GitHub Actions: Detect either head branch in Pull Requests or short ref name (vs fully-formed ref) in the other cases

https://github.com/KnapsackPro/knapsack_pro-ruby/pull/308
Expand Down
2 changes: 0 additions & 2 deletions lib/knapsack_pro.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,10 @@
require_relative 'knapsack_pro/queue_allocator'
require_relative 'knapsack_pro/mask_string'
require_relative 'knapsack_pro/test_suite'
require_relative 'knapsack_pro/test_case_mergers/base_merger'
require_relative 'knapsack_pro/test_case_mergers/rspec_merger'
require_relative 'knapsack_pro/build_distribution_fetcher'
require_relative 'knapsack_pro/slow_test_file_determiner'
require_relative 'knapsack_pro/slow_test_file_finder'
require_relative 'knapsack_pro/test_files_with_test_cases_composer'
require_relative 'knapsack_pro/base_allocator_builder'
require_relative 'knapsack_pro/regular_allocator_builder'
require_relative 'knapsack_pro/queue_allocator_builder'
Expand Down
18 changes: 1 addition & 17 deletions lib/knapsack_pro/adapters/base_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,10 @@ def self.split_by_test_cases_enabled?
false
end

def self.test_file_cases_for(slow_test_files)
def self.calculate_slow_id_paths
raise NotImplementedError
end

def self.slow_test_file?(adapter_class, test_file_path)
@slow_test_file_paths ||=
begin
slow_test_files =
if KnapsackPro::Config::Env.slow_test_file_pattern
KnapsackPro::TestFileFinder.slow_test_files_by_pattern(adapter_class)
else
# get slow test files from JSON file based on data from API
KnapsackPro::SlowTestFileDeterminer.read_from_json_report
end
KnapsackPro::TestFilePresenter.paths(slow_test_files)
end
clean_path = KnapsackPro::TestFileCleaner.clean(test_file_path)
@slow_test_file_paths.include?(clean_path)
end

def self.bind
adapter = new
adapter.bind
Expand Down
20 changes: 11 additions & 9 deletions lib/knapsack_pro/adapters/rspec_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,17 @@ def self.split_by_test_cases_enabled?
true
end

def self.test_file_cases_for(slow_test_files)
KnapsackPro.logger.info("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing #{slow_test_files.size} slow test files.")

# generate the RSpec JSON report in a separate process to not pollute the RSpec state
def self.calculate_slow_id_paths
# Shell out not to pollute the RSpec state
cmd = [
'RACK_ENV=test',
'RAILS_ENV=test',
KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
'rake knapsack_pro:rspec_test_example_detector',
].join(' ')
unless Kernel.system(cmd)
raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
end
raise "Failed to calculate Split by Test Examples: #{cmd}" unless Kernel.system(cmd)

# read the JSON report
KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.test_file_example_paths
KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.slow_id_paths!
end

def self.has_format_option?(cli_args)
Expand Down Expand Up @@ -86,6 +81,13 @@ def self.id_path?(path)
!id.nil?
end

def self.concat_paths(test_files, id_paths)
paths = KnapsackPro::TestFilePresenter.paths(test_files)
file_paths = id_paths.map { |id_path| parse_file_path(id_path) }
acc = paths + id_paths - file_paths
KnapsackPro::TestFilePresenter.test_files(acc)
end

def self.rails_helper_exists?(test_dir)
File.exist?("#{test_dir}/rails_helper.rb")
end
Expand Down
24 changes: 14 additions & 10 deletions lib/knapsack_pro/build_distribution_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,15 @@ def self.call
new.call
end

# get test files and time execution for last build distribution matching:
# branch, node_total, node_index
def call
connection = KnapsackPro::Client::Connection.new(build_action)
response = connection.call
if connection.success?
raise ArgumentError.new(response) if connection.errors?
BuildDistributionEntity.new(response)
else
KnapsackPro.logger.warn("Slow test files fallback behaviour started. We could not connect with Knapsack Pro API to fetch last CI build test files that are needed to determine slow test files. No test files will be split by test cases. It means all test files will be split by the whole test files as if split by test cases would be disabled #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}")
BuildDistributionEntity.new({
'time_execution' => 0.0,
'test_files' => [],
})
KnapsackPro.logger.warn("Failed to fetch slow test files. Split by Test Examples disabled. See: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}")
BuildDistributionEntity.new({ 'time_execution' => 0.0, 'test_files' => [] })
end
end

Expand All @@ -48,12 +43,21 @@ def repository_adapter
end

def build_action
KnapsackPro::Client::API::V1::BuildDistributions.last(
request_hash = {
commit_hash: repository_adapter.commit_hash,
branch: repository_adapter.branch,
node_total: KnapsackPro::Config::Env.ci_node_total,
node_index: KnapsackPro::Config::Env.ci_node_index,
)
node_index: KnapsackPro::Config::Env.ci_node_index
}

if ENV['KNAPSACK_PRO_PRECALCULATING_SPLIT_BY_TEST_EXAMPLES']
request_hash.merge!(
node_build_id: KnapsackPro::Config::Env.ci_node_build_id,
none_if_queue_initialized: true
)
end

KnapsackPro::Client::API::V1::BuildDistributions.last(request_hash)
end
end
end
9 changes: 2 additions & 7 deletions lib/knapsack_pro/client/api/v1/build_distributions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,11 @@ def subset(args)
)
end

def last(args)
def last(request_hash)
action_class.new(
endpoint_path: '/v1/build_distributions/last',
http_method: :get,
request_hash: {
:commit_hash => args.fetch(:commit_hash),
:branch => args.fetch(:branch),
:node_total => args.fetch(:node_total),
:node_index => args.fetch(:node_index),
}
request_hash: request_hash
)
end
end
Expand Down
16 changes: 8 additions & 8 deletions lib/knapsack_pro/config/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ def slow_test_file_pattern
ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN']
end

def slow_test_file_threshold
ENV.fetch('KNAPSACK_PRO_SLOW_TEST_FILE_THRESHOLD', nil)&.to_f
end

def slow_test_file_threshold?
!!slow_test_file_threshold
end

def test_file_exclude_pattern
ENV['KNAPSACK_PRO_TEST_FILE_EXCLUDE_PATTERN']
end
Expand Down Expand Up @@ -200,14 +208,6 @@ def rspec_test_example_detector_prefix
ENV.fetch('KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX', 'bundle exec')
end

def slow_test_file_threshold
ENV.fetch('KNAPSACK_PRO_SLOW_TEST_FILE_THRESHOLD', nil)&.to_f
end

def slow_test_file_threshold?
!!slow_test_file_threshold
end

def test_suite_token
env_name = 'KNAPSACK_PRO_TEST_SUITE_TOKEN'
ENV[env_name] || raise("Missing environment variable #{env_name}. You should set environment variable like #{env_name}_RSPEC (note there is suffix _RSPEC at the end). knapsack_pro gem will set #{env_name} based on #{env_name}_RSPEC value. If you use other test runner than RSpec then use proper suffix.")
Expand Down
22 changes: 0 additions & 22 deletions lib/knapsack_pro/slow_test_file_determiner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,5 @@ def self.call(test_files)
execution_time >= KnapsackPro::Config::Env.slow_test_file_threshold
end
end

def self.save_to_json_report(test_files)
KnapsackPro::Config::TempFiles.ensure_temp_directory_exists!
FileUtils.mkdir_p(report_dir)
File.write(report_path, test_files.to_json)
end

def self.read_from_json_report
raise "The report with slow test files has not been generated yet. If you have enabled split by test cases #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES} and you see this error it means that your tests accidentally cleaned up the .knapsack_pro directory. Please do not remove this directory during tests runtime!" unless File.exist?(report_path)
slow_test_files_json_report = File.read(report_path)
JSON.parse(slow_test_files_json_report)
end

private

def self.report_path
"#{report_dir}/slow_test_files_node_#{KnapsackPro::Config::Env.ci_node_index}.json"
end

def self.report_dir
"#{KnapsackPro::Config::TempFiles::TEMP_DIRECTORY_PATH}/slow_test_file_determiner"
end
end
end
12 changes: 2 additions & 10 deletions lib/knapsack_pro/slow_test_file_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,11 @@ def self.call(adapter_class)
raise "Split by test cases is not possible when you have enabled test file names encryption ( #{KnapsackPro::Urls::ENCRYPTION} ). You need to disable encryption with KNAPSACK_PRO_TEST_FILES_ENCRYPTED=false in order to use split by test cases #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
end

# get list of recorded test files for last CI Build
build_distribution_entity = KnapsackPro::BuildDistributionFetcher.call
test_files_from_api = build_distribution_entity.test_files

merged_test_files_from_api = KnapsackPro::TestCaseMergers::BaseMerger.call(adapter_class, test_files_from_api)

merged_test_files_from_api = KnapsackPro::TestCaseMergers::RSpecMerger.new(test_files_from_api).call
test_files_existing_on_disk = KnapsackPro::TestFileFinder.select_test_files_that_can_be_run(adapter_class, merged_test_files_from_api)

slow_test_files = KnapsackPro::SlowTestFileDeterminer.call(test_files_existing_on_disk)

KnapsackPro::SlowTestFileDeterminer.save_to_json_report(slow_test_files)

slow_test_files
KnapsackPro::SlowTestFileDeterminer.call(test_files_existing_on_disk)
end
end
end
Loading