gitlab_quality-test_tooling 1.7.0 → 1.8.1
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +10 -6
- data/exe/relate-failure-issue +4 -0
- data/lib/gitlab_quality/test_tooling/report/relate_failure_issue.rb +28 -4
- data/lib/gitlab_quality/test_tooling/report/report_as_issue.rb +9 -0
- data/lib/gitlab_quality/test_tooling/report/slow_test_issue.rb +9 -1
- data/lib/gitlab_quality/test_tooling/test_metric/json_test_metric.rb +46 -0
- data/lib/gitlab_quality/test_tooling/test_metrics/json_test_metric_collection.rb +45 -0
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4c8eda4a61584293cfb2f20a4ece0c5afd8963be75c56c5c3a1f71cbbf04a2ed
|
|
4
|
+
data.tar.gz: 72ea08e5a2fac3b5d9f0d5dada3bb71c7b655acbd7c03f4533e0bbe951be954e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fe64d9f93ff1343c36d1d478f63e9a4614587897b6414ec3a1f4c28fdd90ee1d245134e9350f546373350acf1f1355dd50d31da162a899d096ceb1114029767e
|
|
7
|
+
data.tar.gz: d4dd2ba2714e54c766592734481f7ba373692375f4c2011578f1b22da36fae3b8d58e721677e47ba214805d3798737c5ad6dec7711d586a46727029e555fb72e
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -77,6 +77,8 @@ Usage: exe/prepare-stage-reports [options]
|
|
|
77
77
|
Purpose: Relate test failures to failure issues from RSpec report files (JSON or JUnit XML)
|
|
78
78
|
Usage: exe/relate-failure-issue [options]
|
|
79
79
|
-i, --input-files INPUT_FILES RSpec report files (JSON or JUnit XML)
|
|
80
|
+
-m METRICS_FILES, Test metrics files (JSON)
|
|
81
|
+
--metrics-files
|
|
80
82
|
--max-diff-ratio MAX_DIFF_RATO
|
|
81
83
|
Max stacktrace diff ratio for failure issues detection
|
|
82
84
|
-p, --project PROJECT Can be an integer or a group/project string
|
|
@@ -128,11 +130,13 @@ Usage: exe/update-screenshot-paths [options]
|
|
|
128
130
|
|
|
129
131
|
```shell
|
|
130
132
|
Purpose: Create slow test issues from JSON RSpec report files
|
|
131
|
-
Usage: exe/slow-test-
|
|
133
|
+
Usage: exe/slow-test-issues [options]
|
|
132
134
|
-i, --input-files INPUT_FILES JSON RSpec report files JSON
|
|
133
135
|
-p, --project PROJECT Can be an integer or a group/project string
|
|
134
136
|
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
|
135
|
-
|
|
137
|
+
-r RELATED_ISSUES_FILE, The file path for the related issues
|
|
138
|
+
--related-issues-file
|
|
139
|
+
--dry-run Perform a dry-run (don't create issues)
|
|
136
140
|
-v, --version Show the version
|
|
137
141
|
-h, --help Show the usage
|
|
138
142
|
```
|
|
@@ -141,13 +145,13 @@ Usage: exe/slow-test-issue [options]
|
|
|
141
145
|
|
|
142
146
|
```shell
|
|
143
147
|
Purpose: Purpose: Create flaky test issues for any passed test coming from rspec-retry JSON report files
|
|
144
|
-
Usage: exe/flaky-test-
|
|
148
|
+
Usage: exe/flaky-test-issues [options]
|
|
145
149
|
-i, --input-files INPUT_FILES JSON rspec-retry report files
|
|
146
150
|
-p, --project PROJECT Can be an integer or a group/project string
|
|
147
151
|
-m MERGE_REQUEST_IID, An integer merge request IID
|
|
148
152
|
--merge_request_iid
|
|
149
153
|
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
|
150
|
-
--dry-run Perform a dry-run (don't create
|
|
154
|
+
--dry-run Perform a dry-run (don't create issues)
|
|
151
155
|
-v, --version Show the version
|
|
152
156
|
-h, --help Show the usage
|
|
153
157
|
```
|
|
@@ -158,11 +162,11 @@ Usage: exe/flaky-test-issue [options]
|
|
|
158
162
|
Purpose: Create slow test note on merge requests from JSON RSpec report files
|
|
159
163
|
Usage: exe/slow-test-merge-request-report-note [options]
|
|
160
164
|
-i, --input-files INPUT_FILES JSON RSpec report files JSON
|
|
161
|
-
|
|
165
|
+
-p, --project PROJECT Can be an integer or a group/project string
|
|
162
166
|
-m MERGE_REQUEST_IID, An integer merge request IID
|
|
163
167
|
--merge_request_iid
|
|
164
168
|
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
|
165
|
-
--dry-run Perform a dry-run (don't create
|
|
169
|
+
--dry-run Perform a dry-run (don't create note)
|
|
166
170
|
-v, --version Show the version
|
|
167
171
|
-h, --help Show the usage
|
|
168
172
|
```
|
data/exe/relate-failure-issue
CHANGED
|
@@ -15,6 +15,10 @@ options = OptionParser.new do |opts|
|
|
|
15
15
|
params[:input_files] = input_files
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
opts.on('-m', '--metrics-files METRICS_FILES', String, 'Test metrics files (JSON)') do |metrics_files|
|
|
19
|
+
params[:metrics_files] = metrics_files
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
opts.on('--max-diff-ratio MAX_DIFF_RATO', Float, 'Max stacktrace diff ratio for failure issues detection') do |max_diff_ratio|
|
|
19
23
|
params[:max_diff_ratio] = max_diff_ratio
|
|
20
24
|
end
|
|
@@ -41,6 +41,7 @@ module GitlabQuality
|
|
|
41
41
|
system_logs: [],
|
|
42
42
|
base_issue_labels: nil,
|
|
43
43
|
exclude_labels_for_search: nil,
|
|
44
|
+
metrics_files: [],
|
|
44
45
|
**kwargs)
|
|
45
46
|
super
|
|
46
47
|
@max_diff_ratio = max_diff_ratio.to_f
|
|
@@ -49,11 +50,12 @@ module GitlabQuality
|
|
|
49
50
|
@exclude_labels_for_search = Set.new(exclude_labels_for_search)
|
|
50
51
|
@issue_type = 'issue'
|
|
51
52
|
@commented_issue_list = Set.new
|
|
53
|
+
@metrics_files = Array(metrics_files)
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
private
|
|
55
57
|
|
|
56
|
-
attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search
|
|
58
|
+
attr_reader :max_diff_ratio, :system_logs, :base_issue_labels, :exclude_labels_for_search, :metrics_files
|
|
57
59
|
|
|
58
60
|
def run!
|
|
59
61
|
puts "Reporting test failures in `#{files.join(',')}` as issues in project `#{project}` via the API at `#{Runtime::Env.gitlab_api_base}`."
|
|
@@ -66,16 +68,39 @@ module GitlabQuality
|
|
|
66
68
|
write_issues_log_file
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
def test_metric_collections
|
|
72
|
+
@test_metric_collections ||= Dir.glob(metrics_files).map do |path|
|
|
73
|
+
TestMetrics::JsonTestMetricCollection.new(path)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
69
77
|
def process_test_results(test_results)
|
|
70
78
|
systemic_failures = systemic_failures_for_test_results(test_results)
|
|
71
79
|
|
|
72
80
|
test_results.each do |test|
|
|
73
81
|
collect_issues(test, relate_failure_to_issue(test)) if should_report?(test, systemic_failures)
|
|
82
|
+
|
|
83
|
+
copy_failure_issue_to_test_metrics(test) if metrics_files.any?
|
|
74
84
|
end
|
|
75
85
|
|
|
76
86
|
test_results.write
|
|
77
87
|
end
|
|
78
88
|
|
|
89
|
+
def copy_failure_issue_to_test_metrics(test)
|
|
90
|
+
failure_issue = test.failure_issue
|
|
91
|
+
|
|
92
|
+
return unless failure_issue
|
|
93
|
+
|
|
94
|
+
test_metric_collections.find do |test_metric_collection|
|
|
95
|
+
test_metric = test_metric_collection.metric_for_test_id(test.example_id)
|
|
96
|
+
|
|
97
|
+
if test_metric
|
|
98
|
+
test_metric.fields['failure_issue'] = failure_issue
|
|
99
|
+
test_metric_collection.write
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
79
104
|
def systemic_failures_for_test_results(test_results)
|
|
80
105
|
test_results
|
|
81
106
|
.flat_map { |test| test.failures.map { |failure| failure['message'].lines.first.chomp } }
|
|
@@ -142,9 +167,7 @@ module GitlabQuality
|
|
|
142
167
|
def pipeline_issues_with_similar_stacktrace(test)
|
|
143
168
|
search_labels = (base_issue_labels + Set.new(%w[test failure::new])).to_a
|
|
144
169
|
not_labels = exclude_labels_for_search.to_a
|
|
145
|
-
|
|
146
|
-
not: { labels: not_labels },
|
|
147
|
-
created_after: past_timestamp(2) }).select do |issue|
|
|
170
|
+
find_issues_created_after(past_timestamp(2), state: 'opened', labels: search_labels, not_labels: not_labels).select do |issue|
|
|
148
171
|
job_url_from_issue = failed_issue_job_url(issue)
|
|
149
172
|
|
|
150
173
|
next if pipeline != pipeline_env_from_job_url(job_url_from_issue)
|
|
@@ -171,6 +194,7 @@ module GitlabQuality
|
|
|
171
194
|
def failure_issues(test)
|
|
172
195
|
find_issues_for_test(
|
|
173
196
|
test,
|
|
197
|
+
state: 'opened',
|
|
174
198
|
labels: base_issue_labels + Set.new(%w[test]),
|
|
175
199
|
not_labels: exclude_labels_for_search
|
|
176
200
|
)
|
|
@@ -145,10 +145,19 @@ module GitlabQuality
|
|
|
145
145
|
def find_issues_for_test(test, labels:, not_labels: Set.new, state: nil)
|
|
146
146
|
search_options = { labels: labels.to_a, not: { labels: not_labels.to_a } }
|
|
147
147
|
search_options[:state] = state if state
|
|
148
|
+
search_options[:search] = test.file.to_s.empty? ? test.name : partial_file_path(test.file)
|
|
149
|
+
search_options[:in] = 'title'
|
|
148
150
|
|
|
149
151
|
gitlab.find_issues(options: search_options).find_all { |issue| issue_match_test?(issue, test) }
|
|
150
152
|
end
|
|
151
153
|
|
|
154
|
+
def find_issues_created_after(timestamp, labels:, not_labels: Set.new, state: nil)
|
|
155
|
+
search_options = { labels: labels.to_a, not: { labels: not_labels.to_a }, created_after: timestamp }
|
|
156
|
+
search_options[:state] = state if state
|
|
157
|
+
|
|
158
|
+
gitlab.find_issues(options: search_options)
|
|
159
|
+
end
|
|
160
|
+
|
|
152
161
|
def issue_match_test?(issue, test)
|
|
153
162
|
issue_title = issue.title.strip
|
|
154
163
|
test_file_path_found = !test.file.to_s.empty? && issue_title.include?(partial_file_path(test.file))
|
|
@@ -65,10 +65,18 @@ module GitlabQuality
|
|
|
65
65
|
"1. #{Time.new.utc.strftime('%F')}: #{test.ci_job_url} (#{ENV.fetch('CI_PIPELINE_URL', 'pipeline url is missing')})"
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
def slow_test_issues(test)
|
|
69
|
+
find_issues_for_test(
|
|
70
|
+
test,
|
|
71
|
+
state: 'opened',
|
|
72
|
+
labels: SEARCH_LABELS
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
68
76
|
def create_slow_issue(test)
|
|
69
77
|
puts " => Finding existing issues for slow test '#{test.name}' (run time: #{test.run_time} seconds)..."
|
|
70
78
|
|
|
71
|
-
issues =
|
|
79
|
+
issues = slow_test_issues(test)
|
|
72
80
|
|
|
73
81
|
if issues.blank?
|
|
74
82
|
issues << create_issue(test)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitlabQuality
|
|
4
|
+
module TestTooling
|
|
5
|
+
module TestMetric
|
|
6
|
+
class JsonTestMetric
|
|
7
|
+
attr_reader :metric
|
|
8
|
+
|
|
9
|
+
def initialize(metric)
|
|
10
|
+
@metric = metric
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def name
|
|
14
|
+
metric.fetch('name')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def time
|
|
18
|
+
metric.fetch('time')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def tags
|
|
22
|
+
@tags ||= metric.fetch('tags')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def fields
|
|
26
|
+
@fields ||= metric.fetch('fields')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_json(*options)
|
|
30
|
+
as_json.to_json(*options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def as_json
|
|
36
|
+
{
|
|
37
|
+
name: name,
|
|
38
|
+
time: time,
|
|
39
|
+
tags: tags,
|
|
40
|
+
fields: fields
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module GitlabQuality
|
|
6
|
+
module TestTooling
|
|
7
|
+
module TestMetrics
|
|
8
|
+
class JsonTestMetricCollection
|
|
9
|
+
include Enumerable
|
|
10
|
+
|
|
11
|
+
attr_reader :path, :metrics
|
|
12
|
+
|
|
13
|
+
def initialize(path)
|
|
14
|
+
@path = path
|
|
15
|
+
@metrics = process
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def metric_for_test_id(test_id)
|
|
19
|
+
metrics.find do |metric|
|
|
20
|
+
metric.fields['id'] == test_id
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def write
|
|
25
|
+
File.write(path, JSON.pretty_generate(metrics))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def parse
|
|
31
|
+
JSON.parse(File.read(path))
|
|
32
|
+
rescue JSON::ParserError
|
|
33
|
+
Runtime::Logger.debug("#{self.class.name}##{__method__} attempted to parse invalid JSON at path: #{path}")
|
|
34
|
+
{}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def process
|
|
38
|
+
parse.map do |test|
|
|
39
|
+
GitlabQuality::TestTooling::TestMetric::JsonTestMetric.new(test)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
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.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitLab Quality
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-12-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: climate_control
|
|
@@ -422,6 +422,8 @@ files:
|
|
|
422
422
|
- lib/gitlab_quality/test_tooling/system_logs/log_types/rails/graphql_log.rb
|
|
423
423
|
- lib/gitlab_quality/test_tooling/system_logs/shared_fields.rb
|
|
424
424
|
- lib/gitlab_quality/test_tooling/system_logs/system_logs_formatter.rb
|
|
425
|
+
- lib/gitlab_quality/test_tooling/test_metric/json_test_metric.rb
|
|
426
|
+
- lib/gitlab_quality/test_tooling/test_metrics/json_test_metric_collection.rb
|
|
425
427
|
- lib/gitlab_quality/test_tooling/test_result/base_test_result.rb
|
|
426
428
|
- lib/gitlab_quality/test_tooling/test_result/j_unit_test_result.rb
|
|
427
429
|
- lib/gitlab_quality/test_tooling/test_result/json_test_result.rb
|