# frozen_string_literal: true module GitlabQuality module TestTooling module Report # Uses the API to create GitLab issues for slow tests # # - Takes the JSON test reports like rspec-*.json` # - Takes a project where slow issues should be created # - Find issue by title (with test description or test file) # - Add test metadata, duration to the issue with group and category labels class SlowTestIssue < ReportAsIssue include Concerns::FindSetDri include Concerns::GroupAndCategoryLabels NEW_ISSUE_LABELS = Set.new(%w[test type::maintenance maintenance::performance priority::3 severity::3]).freeze SEARCH_LABELS = %w[test maintenance::performance].freeze MultipleIssuesFound = Class.new(StandardError) TestLevelSpecification = Struct.new(:regex, :max_duration) OTHER_TESTS_MAX_DURATION = 45.40 # seconds TEST_LEVEL_SPECIFICATIONS = [ TestLevelSpecification.new(%r{/features/}, 50.13), TestLevelSpecification.new(%r{/controllers|requests/}, 19.20), TestLevelSpecification.new(%r{/lib/}, 27.12) ].freeze private def run! puts "Reporting slow tests in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`." TestResults::Builder.new(files).test_results_per_file do |test_results| puts "=> Reporting #{test_results.count} tests in #{test_results.path}" test_results.each do |test| create_slow_issue(test) if should_create_slow_issue?(test) end end end def new_issue_title(test) "Slow test in #{super}" end def new_issue_description(test) super + [ "\n### Slow test", "Slow tests detected, see guides for more details and how to improve them:", "- [Top slow tests](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#top-slow-tests)", "- [Test speed](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#test-speed)", "**Duration**: #{test.run_time} seconds" ].compact.join("\n\n") end def create_slow_issue(test) puts " => Finding existing issues for slow test '#{test.name}' (run time: #{test.run_time} seconds)..." issue = find_issue(test) puts " => Existing issue link #{issue['web_url']}" if issue.present? create_issue(test) unless issue.present? rescue MultipleIssuesFound => e warn(e.message) end def find_issue(test) search_labels = SEARCH_LABELS gitlab.find_issues(options: { state: 'opened', labels: search_labels.to_a }).find do |issue| issue_title = issue.title.strip issue_title.include?(test.name) || issue_title.include?(partial_file_path(test.file)) end end def should_create_slow_issue?(test) test.run_time > max_duration_for_test(test) end def max_duration_for_test(test) test_level_specification = TEST_LEVEL_SPECIFICATIONS.find do |test_level_specification| test.example_id =~ test_level_specification.regex end return OTHER_TESTS_MAX_DURATION unless test_level_specification test_level_specification.max_duration end end end end end