gitlab_quality-test_tooling 1.32.1 → 1.34.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time.rb +38 -4
- data/lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time_report.rb +5 -1
- data/lib/gitlab_quality/test_tooling/labels_inference.rb +0 -1
- data/lib/gitlab_quality/test_tooling/report/concerns/group_and_category_labels.rb +7 -4
- data/lib/gitlab_quality/test_tooling/report/knapsack_report_issue.rb +7 -4
- data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +2 -2
- data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +3 -3
- data/lib/gitlab_quality/test_tooling/test_meta/test_meta_updater.rb +1 -2
- data/lib/gitlab_quality/test_tooling/test_result/base_test_result.rb +51 -4
- data/lib/gitlab_quality/test_tooling/test_result/j_unit_test_result.rb +1 -52
- data/lib/gitlab_quality/test_tooling/test_result/json_test_result.rb +0 -51
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d3f45e3af6f11c3ebca5f44f3d717960c1ac20cffdc13db75a16d68c9eecfbd
|
4
|
+
data.tar.gz: 0edf629fc7f432a779141be67a231887224a78793defedad416df50b327ecbcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7570cde0cd99ea0fe3148f9a8c1f9a7035c165fb0f5506623a79d3f2c3227581d6de878fd56c9a5d5f4c4bae22191d688f49355c74e4b4926b6362ef5d3f8d06
|
7
|
+
data.tar.gz: 0112df035902806c60a82433118669b43bcd9feff89fd8dc7061ee6ffcda9c53ead791c7df4c188567b2b16ee3be8880e269088f4bee944b944aa2dafb60bfef
|
data/Gemfile.lock
CHANGED
@@ -4,19 +4,39 @@ module GitlabQuality
|
|
4
4
|
module TestTooling
|
5
5
|
module KnapsackReports
|
6
6
|
class SpecRunTime
|
7
|
-
attr_reader :
|
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
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
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
|
@@ -110,6 +109,10 @@ module GitlabQuality
|
|
110
109
|
description: updated_description
|
111
110
|
}
|
112
111
|
|
112
|
+
# We reopen closed issues to not lose any history
|
113
|
+
state_event = issue.state == 'closed' ? 'reopen' : nil
|
114
|
+
issue_attrs[:state_event] = state_event if state_event
|
115
|
+
|
113
116
|
gitlab.edit_issue(iid: issue.iid, options: issue_attrs)
|
114
117
|
puts " => Added a report in #{issue.web_url}!"
|
115
118
|
end
|
@@ -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
|
47
|
+
when 6099..Float::INFINITY
|
48
48
|
'/label ~"slowness::1"'
|
49
|
-
when
|
49
|
+
when 2177..6098
|
50
50
|
'/label ~"slowness::2"'
|
51
|
-
when
|
51
|
+
when 521..2176
|
52
52
|
'/label ~"slowness::3"'
|
53
53
|
else
|
54
54
|
'/label ~"slowness::4"'
|
@@ -11,7 +11,6 @@ module GitlabQuality
|
|
11
11
|
attr_reader :project, :ref, :report_issue, :processed_commits
|
12
12
|
|
13
13
|
TEST_PLATFORM_MAINTAINERS_SLACK_CHANNEL_ID = 'C0437FV9KBN' # test-platform-maintainers
|
14
|
-
BATCH_LIMIT = 10
|
15
14
|
|
16
15
|
def initialize(token:, project:, specs_file:, processor:, ref: 'master', dry_run: false)
|
17
16
|
@specs_file = specs_file
|
@@ -29,7 +28,7 @@ module GitlabQuality
|
|
29
28
|
|
30
29
|
contents['specs'].each do |spec|
|
31
30
|
processor.create_commit(spec, self)
|
32
|
-
break if processed_commits.keys.count >= BATCH_LIMIT
|
31
|
+
break if processed_commits.keys.count >= ENV.fetch('BATCH_LIMIT', 10)
|
33
32
|
end
|
34
33
|
|
35
34
|
processor.create_merge_requests(self)
|
@@ -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
|
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.
|
4
|
+
version: 1.34.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-
|
11
|
+
date: 2024-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|