diff options
Diffstat (limited to 'scripts/determine-qa-tests')
-rwxr-xr-x | scripts/determine-qa-tests | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/scripts/determine-qa-tests b/scripts/determine-qa-tests new file mode 100755 index 00000000000..b1e9d8e9312 --- /dev/null +++ b/scripts/determine-qa-tests @@ -0,0 +1,106 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'optparse' + +# This script returns end-to-end tests or test directories. The returned value is stored in QA_TESTS +# variable for executing only those tests. + +class DetermineQATests # rubocop:disable Gitlab/NamespacedClass + def initialize(options) + @changed_files = options.delete(:changed_files) + @mr_labels = options.delete(:mr_labels) || [] + end + + def execute + # If only e2e test files have changed, run only those tests + qa_tests = if has_qa_spec_only_changes? + changed_files + + # If only non qa files have changed, use the devops MR label to run the files in the related directory + # However, if a feature flag file has changed, do not return any specific test/test directory + elsif has_non_qa_only_changes? && mr_labels.any? && !has_dev_ops_feature_flag_changes? + devops_stage = devops_stage_from_mr_labels + + qa_spec_directories_for_devops_stage(devops_stage) if devops_stage + end + + trim_path(qa_tests).join(' ') if qa_tests + end + + private + + attr_reader :changed_files, :mr_labels + + # Are the changed files only qa specs? + # + # @return [Boolean] whether the changes files are only qa specs + def has_qa_spec_only_changes? + changed_files.all? { |file_path| file_path =~ %r{^qa/qa/specs/features/} } + end + + # Are the changed files only outside the qa directory? + # + # @return [Boolean] whether the changes files are outside of qa directory + def has_non_qa_only_changes? + changed_files.none? { |file_path| file_path =~ %r{^qa/} } + end + + # Are the changed files for development and ops feature flags? + # + # @return [Boolean] whether the changes files are for development and ops feature flags + def has_dev_ops_feature_flag_changes? + changed_files.any? { |file_path| file_path =~ %r{/feature_flags/(development|ops)/.*\.yml} } + end + + # Remove the leading `qa/` from the file or directory paths + # + # @param [Array] paths Array of file or directory paths + # @return [Array] Array of files or directories with the first occurance of `qa/` removed + def trim_path(paths) + paths.map { |path| path.delete_prefix("qa/") } + end + + # Extract devops stage from MR labels + # + # @return [String] a devops stage + def devops_stage_from_mr_labels + mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::') + end + + # Get qa spec directories for devops stage + # + # @param [String] devops_stage a devops stage + # @return [Array] qa spec directories + def qa_spec_directories_for_devops_stage(devops_stage) + Dir.glob("qa/qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} } + end +end + +if $0 == __FILE__ + options = {} + + OptionParser.new do |opts| + opts.on("-f", "--files CHANGED_FILES_PATH", String, + "A path to a file containing a list of changed files") do |value| + changed_files_path = value + abort("ERROR: The specified changed files path does not exist") unless File.exist?(changed_files_path) + + changed_files = File.read(changed_files_path).split(' ') + abort("ERROR: There are no changed files") if changed_files.empty? + + options[:changed_files] = changed_files + end + + opts.on("-l", "--labels MR_LABELS", String, "A comma separated list of MR labels") do |value| + options[:mr_labels] = Array(value&.split(',')).compact + end + + opts.on("-h", "--help", "Prints this help") do + puts opts + exit + end + end.parse! + + puts DetermineQATests.new(options).execute +end |