gitlab_quality-test_tooling 1.7.0 → 1.8.1
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/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
|