gitlab_quality-test_tooling 1.31.0 → 1.33.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d848eed10080ffc5f609ba56a75e7baf6f60cef5792d66c9c1bf9fdb8dbd2b9
4
- data.tar.gz: 5d799f1628b689fda63e0ca68fb70c5f2e6e37d20d867d6ade61e4ff04ea7e57
3
+ metadata.gz: 19bea0cdd5a0f100cec11b00c6415c39f8ab637165f67171ad6edac7c33128b9
4
+ data.tar.gz: 9727a96a8ca06238ae89bacdb6f3cc88b66451a6190cac8df48fc82102e7c155
5
5
  SHA512:
6
- metadata.gz: 2e881613627b3fb87abf0c00895a8b44919769b222aa1a42c60c81b8f77a79a99232145e60c798f0296d24f53f761cb6a5fd4c7d00d1cb982dcc3c58fd68a833
7
- data.tar.gz: 90b1345308d5f4abb94ab81f16520bf79870de753f570223c5bc45ade1fdb7d2ca099004d682bdbcfe106c3557ebdd093ce0461b493fd98e6e687bb9930b293e
6
+ metadata.gz: 5dfdfea69e7e2f3500b243d83489050e6d0f2a251a1657a43250206a3710c0b848bdbb38ac34e645a2ec1dc4fc19ce4df47afa21a8c038754ac76f6b606cb14f
7
+ data.tar.gz: 7aa7942d0f0f86c75a2f07eea937c70c9f725b4025302778176e0fb0b9a14db41acd983d1202f86b6a59ce6f00b1c6f85ba214d86deb1a947167a57ff82ad3a4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab_quality-test_tooling (1.31.0)
4
+ gitlab_quality-test_tooling (1.33.0)
5
5
  activesupport (>= 7.0, < 7.2)
6
6
  amatch (~> 0.4.1)
7
7
  gitlab (~> 4.19)
@@ -4,19 +4,39 @@ module GitlabQuality
4
4
  module TestTooling
5
5
  module KnapsackReports
6
6
  class SpecRunTime
7
- attr_reader :project, :file, :expected, :actual, :expected_suite_duration, :actual_suite_duration
7
+ attr_reader :file, :expected, :actual, :expected_suite_duration, :actual_suite_duration, :project, :ref
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
-
13
- def initialize(project:, file:, expected:, actual:, expected_suite_duration:, actual_suite_duration:)
14
- @project = project
12
+ FEATURE_CATEGORY_METADATA_REGEX = /(?<=feature_category: :)(?<feature_category>\w+)/
13
+
14
+ def initialize(
15
+ file:,
16
+ expected:,
17
+ actual:,
18
+ expected_suite_duration:,
19
+ actual_suite_duration:,
20
+ token: '',
21
+ project: Runtime::Env.ci_project_path,
22
+ ref: Runtime::Env.ci_commit_ref_name)
15
23
  @file = file
16
24
  @expected = expected.to_f
17
25
  @actual = actual.to_f
18
26
  @expected_suite_duration = expected_suite_duration.to_f
19
27
  @actual_suite_duration = actual_suite_duration.to_f
28
+ @token = token
29
+ @project = project
30
+ @ref = ref
31
+ end
32
+
33
+ def feature_category
34
+ file_lines.each do |line|
35
+ match = FEATURE_CATEGORY_METADATA_REGEX.match(line)
36
+ next unless match
37
+
38
+ break match[:feature_category]
39
+ end
20
40
  end
21
41
 
22
42
  def should_report?
@@ -50,6 +70,8 @@ module GitlabQuality
50
70
 
51
71
  private
52
72
 
73
+ attr_reader :token
74
+
53
75
  def exceed_actual_to_expected_ratio_threshold?
54
76
  actual / expected >= ACTUAL_TO_EXPECTED_SPEC_RUN_TIME_RATIO_THRESHOLD
55
77
  end
@@ -80,6 +102,18 @@ module GitlabQuality
80
102
  def file_link
81
103
  "https://gitlab.com/#{project}/-/blob/#{Runtime::Env.ci_commit_ref_name}/#{file}"
82
104
  end
105
+
106
+ def file_lines
107
+ files_client.file_contents.lines(chomp: true)
108
+ end
109
+
110
+ def files_client
111
+ @files_client ||= GitlabClient::RepositoryFilesClient.new(
112
+ token: token,
113
+ project: project,
114
+ file_path: file,
115
+ ref: ref)
116
+ end
83
117
  end
84
118
  end
85
119
  end
@@ -8,10 +8,11 @@ module GitlabQuality
8
8
  class SpecRunTimeReport
9
9
  attr_reader :project, :expected_report, :actual_report
10
10
 
11
- def initialize(project:, expected_report_path:, actual_report_path:)
11
+ def initialize(project:, expected_report_path:, actual_report_path:, token: '')
12
12
  @project = project
13
13
  @expected_report = parse(expected_report_path)
14
14
  @actual_report = parse(actual_report_path)
15
+ @token = token
15
16
  end
16
17
 
17
18
  def filtered_report
@@ -25,6 +26,7 @@ module GitlabQuality
25
26
  end
26
27
 
27
28
  spec_run_time = SpecRunTime.new(
29
+ token: token,
28
30
  project: project,
29
31
  file: spec_file,
30
32
  expected: expected_run_time,
@@ -39,6 +41,8 @@ module GitlabQuality
39
41
 
40
42
  private
41
43
 
44
+ attr_reader :token
45
+
42
46
  def parse(report_path)
43
47
  JSON.parse(File.read(report_path))
44
48
  end
@@ -10,7 +10,6 @@ module GitlabQuality
10
10
  WWW_GITLAB_COM_SITE = 'https://about.gitlab.com'
11
11
  WWW_GITLAB_COM_GROUPS_JSON = "#{WWW_GITLAB_COM_SITE}/groups.json".freeze
12
12
  WWW_GITLAB_COM_CATEGORIES_JSON = "#{WWW_GITLAB_COM_SITE}/categories.json".freeze
13
- FEATURE_CATEGORY_METADATA_REGEX = /(?<=feature_category: :)\w+/
14
13
 
15
14
  def infer_labels_from_product_group(product_group)
16
15
  [groups_mapping.dig(product_group, 'label')].compact.to_set
@@ -10,11 +10,14 @@ module GitlabQuality
10
10
  end
11
11
 
12
12
  def new_issue_labels(test)
13
- puts " => [DEBUG] product_group: #{test.product_group}; feature_category: #{test.feature_category}"
13
+ debug_line = ' => [DEBUG] '
14
+ debug_line += "product_group: #{test&.product_group}; " if test.respond_to?(:product_group)
15
+ debug_line += "feature_category: #{test&.feature_category}" if test.respond_to?(:feature_category)
16
+ puts debug_line
14
17
 
15
- new_labels = self.class::NEW_ISSUE_LABELS +
16
- labels_inference.infer_labels_from_product_group(test.product_group) +
17
- labels_inference.infer_labels_from_feature_category(test.feature_category)
18
+ new_labels = self.class::NEW_ISSUE_LABELS
19
+ new_labels += labels_inference.infer_labels_from_product_group(test.product_group) if test.respond_to?(:product_group)
20
+ new_labels += labels_inference.infer_labels_from_feature_category(test.feature_category) if test.respond_to?(:feature_category)
18
21
  up_to_date_labels(test: test, new_labels: new_labels)
19
22
  end
20
23
  end
@@ -14,6 +14,8 @@ module GitlabQuality
14
14
  # - If not found:
15
15
  # - Create a new issue with the run time data
16
16
  class KnapsackReportIssue < ReportAsIssue
17
+ include Concerns::GroupAndCategoryLabels
18
+
17
19
  NEW_ISSUE_LABELS = Set.new(['test', 'type::maintenance', 'maintenance::performance', 'priority::3', 'severity::3', 'knapsack_report']).freeze
18
20
  SEARCH_LABELS = %w[test maintenance::performance knapsack_report].freeze
19
21
  JOB_TIMEOUT_EPIC_URL = 'https://gitlab.com/groups/gitlab-org/quality/engineering-productivity/-/epics/19'
@@ -36,6 +38,7 @@ module GitlabQuality
36
38
 
37
39
  def search_and_create_issue
38
40
  filtered_report = KnapsackReports::SpecRunTimeReport.new(
41
+ token: token,
39
42
  project: project,
40
43
  expected_report_path: expected_report_path,
41
44
  actual_report_path: actual_report_path
@@ -67,10 +70,6 @@ module GitlabQuality
67
70
  files.first
68
71
  end
69
72
 
70
- def new_issue_labels(_spec_run_time)
71
- NEW_ISSUE_LABELS
72
- end
73
-
74
73
  def new_issue_title(spec_run_time)
75
74
  "Job timeout risk: #{spec_run_time.file} ran much longer than expected"
76
75
  end
@@ -124,12 +124,12 @@ module GitlabQuality
124
124
  issue_attrs = {}
125
125
 
126
126
  new_description = new_issue_description(test)
127
- issue_attrs[:description] = new_description unless issue.description == new_description
127
+ issue_attrs[:description] = new_description if issue.description != new_description
128
128
 
129
129
  new_labels = up_to_date_labels(test: test, issue: issue).to_a
130
- issue_attrs[:labels] = new_labels unless issue.labels == new_labels
130
+ issue_attrs[:add_labels] = new_labels if (new_labels - issue.labels).any?
131
131
 
132
- gitlab.edit_issue(iid: issue.iid, options: issue_attrs) unless issue_attrs.empty?
132
+ gitlab.edit_issue(iid: issue.iid, options: issue_attrs) if issue_attrs.any?
133
133
  end
134
134
 
135
135
  def issue_labels(issue)
@@ -149,14 +149,14 @@ module GitlabQuality
149
149
  labels |= new_labels.to_set
150
150
  ee_test?(test) ? labels << 'Enterprise Edition' : labels.delete('Enterprise Edition')
151
151
 
152
- if test.quarantine?
152
+ if test.respond_to?(:quarantine?) && test.quarantine?
153
153
  labels << 'quarantine'
154
154
  labels << "quarantine::#{test.quarantine_type}"
155
155
  else
156
156
  labels.delete_if { |label| label.include?('quarantine') }
157
157
  end
158
158
 
159
- labels << 'rspec-shared-examples' if test.calls_shared_examples?
159
+ labels << 'rspec-shared-examples' if test.respond_to?(:calls_shared_examples?) && test.calls_shared_examples?
160
160
 
161
161
  labels
162
162
  end
@@ -44,11 +44,11 @@ module GitlabQuality
44
44
 
45
45
  def health_problem_status_label_quick_action(reports_list)
46
46
  case reports_list.reports_count
47
- when 40..Float::INFINITY
47
+ when 6099..Float::INFINITY
48
48
  '/label ~"slowness::1"'
49
- when 28..39
49
+ when 2177..6098
50
50
  '/label ~"slowness::2"'
51
- when 12..27
51
+ when 521..2176
52
52
  '/label ~"slowness::3"'
53
53
  else
54
54
  '/label ~"slowness::4"'
@@ -50,18 +50,21 @@ module GitlabQuality
50
50
 
51
51
  private
52
52
 
53
- attr_reader :context, :file_path, :file, :file_contents, :example_name, :mr_title, :changed_line_no
53
+ attr_reader :context, :file_path, :file, :file_contents, :example_name, :mr_title, :changed_line_no, :matched_lines
54
54
 
55
55
  # Checks if there is already an MR open
56
56
  #
57
57
  # @return [Boolean]
58
- def proceed_with_commit?
58
+ def proceed_with_commit? # rubocop:disable Metrics/AbcSize
59
59
  if changed_line_no.negative?
60
60
  Runtime::Logger.info(" No lines were changed in #{file_path}. Will not proceed with creating MR.")
61
61
  return false
62
62
  elsif context.commit_processed?(file_path, changed_line_no)
63
63
  Runtime::Logger.info(" Record already processed for #{file_path}:#{changed_line_no}. Will not proceed with creating MR.")
64
64
  return false
65
+ elsif context.quarantined?(matched_lines, file_contents)
66
+ Runtime::Logger.info(" This test is in quarantine: #{file_path}:#{changed_line_no}. Will not proceed with creating MR.")
67
+ return false
65
68
  end
66
69
 
67
70
  true
@@ -120,7 +123,7 @@ module GitlabQuality
120
123
  #
121
124
  # @return [Array<String, Integer>] first value holds the new content, the second value holds the line number of the test
122
125
  def add_metadata # rubocop:disable Metrics/AbcSize
123
- matched_lines = context.find_example_match_lines(file_contents, example_name)
126
+ @matched_lines = context.find_example_match_lines(file_contents, example_name)
124
127
 
125
128
  if matched_lines.any? { |line| line[0].include?(':blocking') }
126
129
  Runtime::Logger.info("Example '#{example_name}' is already blocking")
@@ -60,12 +60,12 @@ module GitlabQuality
60
60
  private
61
61
 
62
62
  attr_reader :context, :file_path, :file_contents, :failure_issue_url, :example_name,
63
- :mr_title, :failure_issue, :changed_line_no
63
+ :mr_title, :failure_issue, :changed_line_no, :matched_lines
64
64
 
65
65
  # Checks if the failure issue is closed or if there is already an MR open
66
66
  #
67
67
  # @return [Boolean]
68
- def proceed_with_commit?
68
+ def proceed_with_commit? # rubocop:disable Metrics/AbcSize
69
69
  if context.issue_is_closed?(failure_issue)
70
70
  Runtime::Logger.info(" Failure issue '#{failure_issue_url}' is closed. Will not proceed with creating MR.")
71
71
  return false
@@ -75,6 +75,9 @@ module GitlabQuality
75
75
  elsif failure_is_related_to_test_environment?
76
76
  Runtime::Logger.info(" Failure issue '#{failure_issue_url}' is environment related. Will not proceed with creating MR.")
77
77
  return false
78
+ elsif context.quarantined?(matched_lines, file_contents)
79
+ Runtime::Logger.info(" This test is already in quarantine: #{file_path}:#{changed_line_no}. Will not proceed with creating MR.")
80
+ return false
78
81
  end
79
82
 
80
83
  true
@@ -199,7 +202,7 @@ module GitlabQuality
199
202
  #
200
203
  # @return [Array<String, Integer>] first value holds the new content, the second value holds the line number of the test
201
204
  def add_metadata # rubocop:disable Metrics/AbcSize
202
- matched_lines = context.find_example_match_lines(file_contents, example_name)
205
+ @matched_lines = context.find_example_match_lines(file_contents, example_name)
203
206
 
204
207
  context.update_matched_line(matched_lines.last, file_contents.dup) do |line|
205
208
  indentation = context.indentation(line)
@@ -129,6 +129,26 @@ module GitlabQuality
129
129
  matched_lines
130
130
  end
131
131
 
132
+ # Scans the content from the matched line until `do` is found to look for quarantine token
133
+ #
134
+ # @param [Array] matched_lines an array of arrays containing the matched line and their index
135
+ # @param [String] file_contents the content of the spec file
136
+ # @return [Bolean]
137
+ def quarantined?(matched_lines, file_contents)
138
+ lines = file_contents.split("\n")
139
+
140
+ matched_lines.each do |matched_line|
141
+ matched_line_starting_index = matched_line[1]
142
+
143
+ lines[matched_line_starting_index..].each do |line|
144
+ return true if line.include?('quarantine: {')
145
+ break if / do$/.match?(line)
146
+ end
147
+ end
148
+
149
+ false
150
+ end
151
+
132
152
  # Update the provided matched_line with content from the block if given
133
153
  #
134
154
  # @param [Array<String, Integer>] matched_line first value holds the line content, the second value holds the line number
@@ -32,10 +32,6 @@ module GitlabQuality
32
32
  raise NotImplementedError
33
33
  end
34
34
 
35
- def line_number
36
- raise NotImplementedError
37
- end
38
-
39
35
  def section
40
36
  raise NotImplementedError
41
37
  end
@@ -52,10 +48,53 @@ module GitlabQuality
52
48
  raise NotImplementedError
53
49
  end
54
50
 
51
+ def product_group
52
+ report['product_group'].to_s
53
+ end
54
+
55
+ def feature_category
56
+ report['feature_category']
57
+ end
58
+
55
59
  def failures?
56
60
  failures.any?
57
61
  end
58
62
 
63
+ def product_group?
64
+ product_group != ''
65
+ end
66
+
67
+ def failure_issue
68
+ report['failure_issue']
69
+ end
70
+
71
+ def failure_issue=(new_failure_issue)
72
+ report['failure_issue'] = new_failure_issue
73
+ end
74
+
75
+ def line_number
76
+ report['line_number']
77
+ end
78
+
79
+ def level
80
+ report['level']
81
+ end
82
+
83
+ def run_time
84
+ report['run_time'].to_f.round(2)
85
+ end
86
+
87
+ def screenshot?
88
+ !!screenshot
89
+ end
90
+
91
+ def quarantine?
92
+ # The value for 'quarantine' could be nil, a hash, a string,
93
+ # or true (if the test just has the :quarantine tag)
94
+ # But any non-nil or false value should means the test is in quarantine
95
+ !!quarantine
96
+ end
97
+
59
98
  def file
60
99
  @file ||= relative_file.start_with?('qa/') ? "qa/#{relative_file}" : relative_file
61
100
  end
@@ -98,6 +137,14 @@ module GitlabQuality
98
137
  private
99
138
 
100
139
  attr_reader :token, :project, :ref
140
+
141
+ def screenshot
142
+ report.fetch('screenshot', nil)
143
+ end
144
+
145
+ def quarantine
146
+ report.fetch('quarantine', nil)
147
+ end
101
148
  end
102
149
  end
103
150
  end
@@ -40,64 +40,13 @@ module GitlabQuality
40
40
  end
41
41
  end
42
42
 
43
- def quarantine
44
- !report['quarantine'].nil?
45
- end
46
-
47
- def quarantine?
48
- # The value for 'quarantine' could be nil, a hash, a string,
49
- # or true (if the test just has the :quarantine tag)
50
- # But any non-nil or false value should means the test is in quarantine
51
- !!quarantine
52
- end
53
-
54
- def line_number
55
- report['line_number']
56
- end
57
-
58
- def level
59
- report['level']
60
- end
61
-
62
- def run_time
63
- report['run_time'].to_f.round(2)
64
- end
65
-
66
- def screenshot
67
- report['screenshot']
68
- end
69
-
70
- def screenshot?
71
- !!screenshot
72
- end
73
-
74
43
  def max_duration_for_test
75
- ""
44
+ ''
76
45
  end
77
46
 
78
47
  def ci_job_url
79
48
  ENV.fetch('CI_JOB_URL', '')
80
49
  end
81
-
82
- def product_group
83
- report['product_group'].to_s
84
- end
85
-
86
- def product_group?
87
- product_group != ''
88
- end
89
-
90
- def feature_category
91
- report['feature_category']
92
- end
93
-
94
- def failure_issue
95
- report['failure_issue']
96
- end
97
-
98
- def failure_issue=(new_failure_issue)
99
- report['failure_issue'] = new_failure_issue
100
- end
101
50
  end
102
51
  end
103
52
  end
@@ -68,21 +68,6 @@ module GitlabQuality
68
68
  report['testcase'] = new_testcase
69
69
  end
70
70
 
71
- def failure_issue
72
- report['failure_issue']
73
- end
74
-
75
- def failure_issue=(new_failure_issue)
76
- report['failure_issue'] = new_failure_issue
77
- end
78
-
79
- def quarantine?
80
- # The value for 'quarantine' could be nil, a hash, a string,
81
- # or true (if the test just has the :quarantine tag)
82
- # But any non-nil or false value should means the test is in quarantine
83
- !!quarantine
84
- end
85
-
86
71
  def quarantine_type
87
72
  quarantine['type'] if quarantine?
88
73
  end
@@ -91,26 +76,10 @@ module GitlabQuality
91
76
  quarantine['issue'] if quarantine?
92
77
  end
93
78
 
94
- def screenshot?
95
- !!screenshot
96
- end
97
-
98
79
  def screenshot_image
99
80
  screenshot['image'] if screenshot?
100
81
  end
101
82
 
102
- def product_group
103
- report['product_group'].to_s
104
- end
105
-
106
- def product_group?
107
- product_group != ''
108
- end
109
-
110
- def feature_category
111
- report['feature_category']
112
- end
113
-
114
83
  def section
115
84
  report['section']
116
85
  end
@@ -119,22 +88,10 @@ module GitlabQuality
119
88
  report['category']
120
89
  end
121
90
 
122
- def run_time
123
- report['run_time'].to_f.round(2)
124
- end
125
-
126
91
  def example_id
127
92
  report['id']
128
93
  end
129
94
 
130
- def line_number
131
- report['line_number']
132
- end
133
-
134
- def level
135
- report['level']
136
- end
137
-
138
95
  def ci_job_id
139
96
  report['ci_job_url'].split('/').last
140
97
  end
@@ -180,14 +137,6 @@ module GitlabQuality
180
137
 
181
138
  private
182
139
 
183
- def quarantine
184
- report.fetch('quarantine', nil)
185
- end
186
-
187
- def screenshot
188
- report.fetch('screenshot', nil)
189
- end
190
-
191
140
  def format_message_lines(message_lines)
192
141
  message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
193
142
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GitlabQuality
4
4
  module TestTooling
5
- VERSION = "1.31.0"
5
+ VERSION = "1.33.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.31.0
4
+ version: 1.33.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-07-15 00:00:00.000000000 Z
11
+ date: 2024-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: climate_control