gitlab_quality-test_tooling 1.29.1 → 1.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 523cbb3e59a928b4e438ec2f97818c6b756b5120d9d61ede7945bd5904de4b79
4
- data.tar.gz: 750bb848167c76df6f6287d119eb211fa8281abfade44334ca1cb72142b03361
3
+ metadata.gz: 5d848eed10080ffc5f609ba56a75e7baf6f60cef5792d66c9c1bf9fdb8dbd2b9
4
+ data.tar.gz: 5d799f1628b689fda63e0ca68fb70c5f2e6e37d20d867d6ade61e4ff04ea7e57
5
5
  SHA512:
6
- metadata.gz: ab633288538b5343309ed30766925b61c05685f457e1772f5c80113a013fd75910509cf76fa01feb9e9bffdf5d764357f221b8f7f53748f17daed1f60b240464
7
- data.tar.gz: 2dbf75f6310a515c0c9eb43301fd6b669ba96e115cbdeec37a2570fc556cac94daac00ec81638aaf1288e465efd4284af167e7d060657eaff788d65aebb92362
6
+ metadata.gz: 2e881613627b3fb87abf0c00895a8b44919769b222aa1a42c60c81b8f77a79a99232145e60c798f0296d24f53f761cb6a5fd4c7d00d1cb982dcc3c58fd68a833
7
+ data.tar.gz: 90b1345308d5f4abb94ab81f16520bf79870de753f570223c5bc45ade1fdb7d2ca099004d682bdbcfe106c3557ebdd093ce0461b493fd98e6e687bb9930b293e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (1.29.1)
4
+ gitlab_quality-test_tooling (1.31.0)
5
5
  activesupport (>= 7.0, < 7.2)
6
6
  amatch (~> 0.4.1)
7
7
  gitlab (~> 4.19)
@@ -330,4 +330,4 @@ DEPENDENCIES
330
330
  webmock (= 3.7.0)
331
331
 
332
332
  BUNDLED WITH
333
- 2.5.6
333
+ 2.5.13
data/exe/post-to-slack CHANGED
@@ -47,6 +47,10 @@ options = OptionParser.new do |opts|
47
47
  summary_table_opts[:sort_direction] = sort_direction.downcase.to_sym
48
48
  end
49
49
 
50
+ opts.on('--hide-passed-tests', 'Used with the `--include-summary-table` flag — Hide passed tests from the summary table') do
51
+ summary_table_opts[:hide_passed_tests] = true
52
+ end
53
+
50
54
  opts.on('-t', '--include-summary-table FILES', String, 'Add a test summary table based on RSpec report files (JUnit XML)') do |files|
51
55
  params[:summary_table_files] = files
52
56
  end
@@ -19,7 +19,9 @@ module GitlabQuality
19
19
  end
20
20
 
21
21
  def file_contents_at_line(line_number)
22
- file_contents.lines(chomp: true)[line_number - 1]
22
+ ignore_gitlab_client_exceptions do
23
+ file_contents.lines(chomp: true)[line_number - 1]
24
+ end
23
25
  end
24
26
  end
25
27
  end
@@ -4,13 +4,14 @@ module GitlabQuality
4
4
  module TestTooling
5
5
  module KnapsackReports
6
6
  class SpecRunTime
7
- attr_reader :file, :expected, :actual, :expected_suite_duration, :actual_suite_duration
7
+ attr_reader :project, :file, :expected, :actual, :expected_suite_duration, :actual_suite_duration
8
8
 
9
9
  ACTUAL_TO_EXPECTED_SPEC_RUN_TIME_RATIO_THRESHOLD = 1.5 # actual run time is longer than expected by 50% +
10
10
  SPEC_WEIGHT_PERCENTAGE_TRESHOLD = 15 # a spec file takes 15%+ of the total test suite run time
11
11
  SUITE_DURATION_THRESHOLD = 70 * 60 # if test suite takes more than 70 minutes, job risks timing out
12
12
 
13
- def initialize(file:, expected:, actual:, expected_suite_duration:, actual_suite_duration:)
13
+ def initialize(project:, file:, expected:, actual:, expected_suite_duration:, actual_suite_duration:)
14
+ @project = project
14
15
  @file = file
15
16
  @expected = expected.to_f
16
17
  @actual = actual.to_f
@@ -77,7 +78,7 @@ module GitlabQuality
77
78
  end
78
79
 
79
80
  def file_link
80
- "#{Runtime::Env.file_base_url}#{file}"
81
+ "https://gitlab.com/#{project}/-/blob/#{Runtime::Env.ci_commit_ref_name}/#{file}"
81
82
  end
82
83
  end
83
84
  end
@@ -6,9 +6,10 @@ module GitlabQuality
6
6
  module TestTooling
7
7
  module KnapsackReports
8
8
  class SpecRunTimeReport
9
- attr_reader :expected_report, :actual_report
9
+ attr_reader :project, :expected_report, :actual_report
10
10
 
11
- def initialize(expected_report_path:, actual_report_path:)
11
+ def initialize(project:, expected_report_path:, actual_report_path:)
12
+ @project = project
12
13
  @expected_report = parse(expected_report_path)
13
14
  @actual_report = parse(actual_report_path)
14
15
  end
@@ -24,6 +25,7 @@ module GitlabQuality
24
25
  end
25
26
 
26
27
  spec_run_time = SpecRunTime.new(
28
+ project: project,
27
29
  file: spec_file,
28
30
  expected: expected_run_time,
29
31
  actual: actual_run_time,
@@ -19,8 +19,6 @@ module GitlabQuality
19
19
  ISSUE_STACKTRACE_REGEX = /##### Stack trace\s*(```)#{FAILURE_STACKTRACE_REGEX}(```)\n*/m
20
20
  DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION = 0.15
21
21
 
22
- MultipleNotesFound = Class.new(StandardError)
23
-
24
22
  def initialize(
25
23
  base_issue_labels: nil,
26
24
  max_diff_ratio: DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION,
@@ -73,9 +71,7 @@ module GitlabQuality
73
71
  relevant_notes = find_relevant_failure_discussion_note(issue: issue, test: test, reports_discussion: reports_discussion)
74
72
  return if relevant_notes.empty?
75
73
 
76
- best_matching_note, smaller_diff_ratio = relevant_notes.min_by { |_, diff_ratio| diff_ratio }
77
-
78
- raise(MultipleNotesFound, %(Too many issues found for test '#{test.name}' (`#{test.file}`)!)) unless relevant_notes.values.count(smaller_diff_ratio) == 1
74
+ best_matching_note, _ = relevant_notes.min_by { |_, diff_ratio| diff_ratio }
79
75
 
80
76
  # Re-instantiate a `Gitlab::ObjectifiedHash` object after having converted it to a hash in #find_relevant_failure_issues above.
81
77
  best_matching_note = Gitlab::ObjectifiedHash.new(best_matching_note)
@@ -16,7 +16,7 @@ module GitlabQuality
16
16
  include Concerns::GroupAndCategoryLabels
17
17
  include Concerns::IssueReports
18
18
 
19
- BASE_SEARCH_LABELS = ['test'].freeze
19
+ BASE_SEARCH_LABELS = ['test'].freeze
20
20
  FOUND_IN_MR_LABEL = '~"found:in MR"'
21
21
  FOUND_IN_MASTER_LABEL = '~"found:master"'
22
22
 
@@ -133,8 +133,6 @@ module GitlabQuality
133
133
  else
134
134
  gitlab.create_issue_note(iid: issue.iid, note: note_body)
135
135
  end
136
- rescue MultipleNotesFound => e
137
- warn(e.message)
138
136
  end
139
137
 
140
138
  def find_or_create_reports_discussion(issue:)
@@ -36,6 +36,7 @@ module GitlabQuality
36
36
 
37
37
  def search_and_create_issue
38
38
  filtered_report = KnapsackReports::SpecRunTimeReport.new(
39
+ project: project,
39
40
  expected_report_path: expected_report_path,
40
41
  actual_report_path: actual_report_path
41
42
  ).filtered_report
@@ -25,12 +25,12 @@ module GitlabQuality
25
25
 
26
26
  private
27
27
 
28
- attr_reader :gitlab_merge_request, :files, :project, :merge_request_iid, :slow_tests
28
+ attr_reader :token, :project, :gitlab_merge_request, :files, :merge_request_iid, :slow_tests
29
29
 
30
30
  def run!
31
31
  puts "Reporting slow tests in MR #{merge_request_iid}"
32
32
 
33
- TestResults::Builder.new(token: @token, project: @project, file_glob: files).test_results_per_file do |test_results|
33
+ TestResults::Builder.new(token: token, project: project, file_glob: files).test_results_per_file do |test_results|
34
34
  puts "=> Reporting #{test_results.count} tests in #{test_results.path}"
35
35
 
36
36
  @slow_tests += slow_related_tests(find_slow_tests(test_results))
@@ -52,7 +52,7 @@ module GitlabQuality
52
52
  def run!
53
53
  puts "Reporting test failures in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
54
54
 
55
- TestResults::Builder.new(token: @token, project: @project, file_glob: files).test_results_per_file do |test_results|
55
+ TestResults::Builder.new(token: token, project: project, file_glob: files).test_results_per_file do |test_results|
56
56
  puts "=> Reporting #{test_results.count} tests in #{test_results.path}"
57
57
  process_test_results(test_results)
58
58
  end
@@ -259,7 +259,7 @@ module GitlabQuality
259
259
 
260
260
  best_matching_issue, smaller_diff_ratio = relevant_issues.min_by { |_, diff_ratio| diff_ratio }
261
261
 
262
- raise(MultipleIssuesFound, %(Too many issues found for test '#{test.name}' (`#{test.file}`)!)) unless relevant_issues.values.count(smaller_diff_ratio) == 1
262
+ raise(MultipleIssuesFound, %(Too many issues found for test '#{test.name}' (`#{test.relative_file}`)!)) unless relevant_issues.values.count(smaller_diff_ratio) == 1
263
263
 
264
264
  # Re-instantiate a `Gitlab::ObjectifiedHash` object after having converted it to a hash in #find_relevant_failure_issues above.
265
265
  best_matching_issue = Gitlab::ObjectifiedHash.new(best_matching_issue)
@@ -9,8 +9,8 @@ module GitlabQuality
9
9
  include Concerns::Utils
10
10
 
11
11
  def initialize(token:, input_files:, related_issues_file: nil, project: nil, confidential: false, dry_run: false, **_kwargs)
12
- @project = project
13
12
  @token = token
13
+ @project = project
14
14
  @gitlab = (dry_run ? GitlabClient::IssuesDryClient : GitlabClient::IssuesClient).new(token: token, project: project)
15
15
  @files = Array(input_files)
16
16
  @confidential = confidential
@@ -29,7 +29,7 @@ module GitlabQuality
29
29
 
30
30
  private
31
31
 
32
- attr_reader :gitlab, :files, :project, :issue_type, :confidential, :issue_logger, :token
32
+ attr_reader :token, :gitlab, :files, :project, :issue_type, :confidential, :issue_logger
33
33
 
34
34
  def run!
35
35
  raise NotImplementedError
@@ -47,7 +47,7 @@ module GitlabQuality
47
47
  # Should not be more than 50 characters if we want it indexed.
48
48
  #
49
49
  # See https://gitlab.com/gitlab-org/ruby/gems/gitlab_quality-test_tooling/-/issues/27#note_1607276486
50
- OpenSSL::Digest.hexdigest('SHA256', "#{test.file}#{test.name}")[..40]
50
+ OpenSSL::Digest.hexdigest('SHA256', "#{test.relative_file}#{test.name}")[..40]
51
51
  end
52
52
 
53
53
  def new_issue_description(test)
@@ -58,8 +58,8 @@ module GitlabQuality
58
58
  | Field | Value |
59
59
  | ------ | ------ |
60
60
  | File URL | #{test.test_file_link} |
61
- | Filename | `#{test.file}` |
62
- | Description | `#{test.name}` |
61
+ | Filename | `#{test.relative_file}` |
62
+ | Description | `` #{test.name} `` |
63
63
  | Test level | `#{test.level}` |
64
64
  | Hash | `#{test_hash(test)}` |
65
65
  | Max expected duration | < #{test.max_duration_for_test} seconds |
@@ -14,7 +14,7 @@ module GitlabQuality
14
14
  def initialize(
15
15
  test_case_project_token:, results_issue_project_token:, input_files:, test_case_project: nil, results_issue_project: nil, dry_run: false,
16
16
  **kwargs)
17
- @results_issue_project_token = results_issue_project_token
17
+ @test_case_project_token = test_case_project_token
18
18
  @testcase_project_reporter = GitlabQuality::TestTooling::Report::ResultsInTestCases.new(
19
19
  token: test_case_project_token, input_files: input_files, project: test_case_project, dry_run: dry_run, **kwargs)
20
20
  @results_issue_project_reporter = GitlabQuality::TestTooling::Report::ResultsInIssues.new(
@@ -32,18 +32,20 @@ module GitlabQuality
32
32
 
33
33
  private
34
34
 
35
+ attr_reader :test_case_project_token
36
+
35
37
  # rubocop:disable Metrics/AbcSize
36
38
  def run!
37
39
  puts "Reporting test results in `#{files.join(',')}` as test cases in project `#{test_case_project}` " \
38
40
  "and issues in project `#{results_issue_project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
39
41
 
40
- TestResults::Builder.new(token: @results_issue_project_token, project: @results_issue_project, file_glob: files).test_results_per_file do |test_results|
42
+ TestResults::Builder.new(token: test_case_project_token, project: test_case_project, file_glob: files).test_results_per_file do |test_results|
41
43
  puts "Reporting tests in #{test_results.path}"
42
44
 
43
45
  test_results.each do |test|
44
46
  next if test.file.include?('/features/sanity/') || test.skipped?
45
47
 
46
- puts "Reporting test: #{test.file} | #{test.name}\n"
48
+ puts "Reporting test: #{test.relative_file} | #{test.name}\n"
47
49
 
48
50
  report_test(test)
49
51
  end
@@ -17,6 +17,7 @@ module GitlabQuality
17
17
  'CI_JOB_URL' => :ci_job_url,
18
18
  'CI_PROJECT_ID' => :ci_project_id,
19
19
  'CI_PROJECT_NAME' => :ci_project_name,
20
+ 'CI_PROJECT_PATH' => :ci_project_path,
20
21
  'CI_PIPELINE_ID' => :ci_pipeline_id,
21
22
  'CI_PIPELINE_URL' => :ci_pipeline_url,
22
23
  'SLACK_QA_CHANNEL' => :slack_qa_channel,
@@ -90,10 +91,6 @@ module GitlabQuality
90
91
  "#{ci_project_name}-#{test_subset}"
91
92
  end
92
93
 
93
- def file_base_url
94
- env_var_value_if_defined('FILE_BASE_URL') || "https://gitlab.com/gitlab-org/gitlab/-/blob/master/"
95
- end
96
-
97
94
  private
98
95
 
99
96
  def enabled?(value, default: true)
@@ -14,6 +14,7 @@ module GitlabQuality
14
14
  def self.collect_results(input_files, **options)
15
15
  sort_by = options[:sort_by]
16
16
  sort_direction = options[:sort_direction]
17
+ hide_passed_tests = options[:hide_passed_tests]
17
18
 
18
19
  stage_wise_results = Dir.glob(input_files).each_with_object([]) do |report_file, stage_wise_results|
19
20
  stage_hash = {}
@@ -21,6 +22,8 @@ module GitlabQuality
21
22
 
22
23
  report_stats = Nokogiri::XML(File.open(report_file)).children[0].attributes
23
24
 
25
+ next if hide_passed_tests && report_stats["failures"].value.to_i.zero? && report_stats["errors"].value.to_i.zero?
26
+
24
27
  stage_hash["Total"] = report_stats["tests"].value
25
28
  stage_hash["Failures"] = report_stats["failures"].value
26
29
  stage_hash["Errors"] = report_stats["errors"].value
@@ -13,7 +13,7 @@ module GitlabQuality
13
13
 
14
14
  attr_reader :report
15
15
 
16
- def initialize(report:, token: nil, project: nil, ref: 'master')
16
+ def initialize(report:, token: '', project: Runtime::Env.ci_project_path, ref: Runtime::Env.ci_commit_ref_name)
17
17
  @report = report
18
18
  @token = token
19
19
  @project = project
@@ -28,7 +28,7 @@ module GitlabQuality
28
28
  raise NotImplementedError
29
29
  end
30
30
 
31
- def file
31
+ def relative_file
32
32
  raise NotImplementedError
33
33
  end
34
34
 
@@ -56,6 +56,18 @@ module GitlabQuality
56
56
  failures.any?
57
57
  end
58
58
 
59
+ def file
60
+ @file ||= relative_file.start_with?('qa/') ? "qa/#{relative_file}" : relative_file
61
+ end
62
+
63
+ def file_base_url
64
+ @file_base_url ||= "https://gitlab.com/#{project}/-/blob/#{ref}/"
65
+ end
66
+
67
+ def test_file_link
68
+ "[`#{file}#L#{line_number}`](#{file_base_url}#{file}#L#{line_number})"
69
+ end
70
+
59
71
  def full_stacktrace
60
72
  failures.each do |failure|
61
73
  message = failure['message'] || ""
@@ -10,7 +10,7 @@ module GitlabQuality
10
10
  report['name']
11
11
  end
12
12
 
13
- def file
13
+ def relative_file
14
14
  report['file']&.delete_prefix('./')
15
15
  end
16
16
 
@@ -98,14 +98,6 @@ module GitlabQuality
98
98
  def failure_issue=(new_failure_issue)
99
99
  report['failure_issue'] = new_failure_issue
100
100
  end
101
-
102
- def test_file_link
103
- return "" if file.nil?
104
-
105
- path_prefix = file.start_with?('qa/') ? 'qa/' : ''
106
-
107
- "[`#{path_prefix}#{file}#L#{line_number}`](#{Runtime::Env.file_base_url}#{path_prefix}#{file}#L#{line_number})"
108
- end
109
101
  end
110
102
  end
111
103
  end
@@ -40,7 +40,7 @@ module GitlabQuality
40
40
  report.fetch('full_description').split('#<').first
41
41
  end
42
42
 
43
- def file
43
+ def relative_file
44
44
  report.fetch('file_path').delete_prefix('./')
45
45
  end
46
46
 
@@ -178,12 +178,6 @@ module GitlabQuality
178
178
  test_level_specification.max_duration
179
179
  end
180
180
 
181
- def test_file_link
182
- path_prefix = file.start_with?('qa/') ? 'qa/' : ''
183
-
184
- "[`#{path_prefix}#{file}#L#{line_number}`](#{Runtime::Env.file_base_url}#{path_prefix}#{file}#L#{line_number})"
185
- end
186
-
187
181
  private
188
182
 
189
183
  def quarantine
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "1.29.1"
5
+ VERSION = "1.31.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab_quality-test_tooling
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.29.1
4
+ version: 1.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab Quality
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-21 00:00:00.000000000 Z
11
+ date: 2024-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control