gitlab_quality-test_tooling 3.17.0 → 3.18.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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +10 -0
- data/exe/test-coverage +83 -63
- data/lib/gitlab_quality/test_tooling/code_coverage/per_test_coverage_exporter.rb +127 -0
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3770b676c5ff745323a47749eb04f305d435cff9769a9b567a1cc76289d5b2bb
|
|
4
|
+
data.tar.gz: 9cab37f5980a19ce80980dec4f97d867ca7bfe6e5524b383c71ea8d01509bbc8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42a46bd31d9d7bdf985ad8d430c02e0d7037e116b426ff6358f089ae619f08680cf091758cbe6b5b229612be29e691a5ee760a57fb87b0da91324860b3939590
|
|
7
|
+
data.tar.gz: 71e1eda39f86d10c71d4d14770a925eaad6dc09d019c1cbcf7d769c6e23a73de79cce7fab02412b53ddfee8d4f6e0fa56fa55ae9880636491ffa9aa1024121ae
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -264,6 +264,12 @@ Usage: exe/feature-readiness-evaluation [options]
|
|
|
264
264
|
|
|
265
265
|
### `exe/test-coverage`
|
|
266
266
|
|
|
267
|
+
Runs in one of three modes:
|
|
268
|
+
|
|
269
|
+
- **Full coverage-metrics export** (default, no `--per-test-coverage` / `--run-aggregation`): reads the LCOV report, Crystalball test map, and test reports, then exports the coverage-metrics and test-file-mapping tables. Requires the full argument set.
|
|
270
|
+
- **Per-test coverage** (`--per-test-coverage GLOB`): inserts per-test rows into `code_coverage.test_coverage_per_file` and runs the daily `test_health_risk` aggregation unless `--skip-aggregation`. Needs only the `--clickhouse-*` options; `--test-reports` and `--jest-quarantine-file` are optional enrichment.
|
|
271
|
+
- **Aggregation only** (`--run-aggregation`): runs the daily `test_health_risk` aggregation and exits. Needs only the `--clickhouse-*` options. Pair with `--skip-aggregation` on batched per-test inserts to aggregate once at the end instead of once per batch.
|
|
272
|
+
|
|
267
273
|
```shell
|
|
268
274
|
Purpose: Export test coverage metrics to ClickHouse
|
|
269
275
|
Usage: exe/test-coverage [options]
|
|
@@ -281,6 +287,10 @@ Options:
|
|
|
281
287
|
ClickHouse shared database name
|
|
282
288
|
--responsibility-patterns PATH
|
|
283
289
|
Path to YAML file with responsibility classification patterns
|
|
290
|
+
--per-test-coverage GLOB Per-test coverage mode (see above). Glob for per-test coverage JSON/NDJSON files.
|
|
291
|
+
--jest-quarantine-file PATH Optional. Path to quarantined_vue3_specs.txt, ingested before the aggregation.
|
|
292
|
+
--skip-aggregation Optional. With --per-test-coverage, insert rows without running the aggregation.
|
|
293
|
+
--run-aggregation Run only the daily test_health_risk aggregation, then exit.
|
|
284
294
|
|
|
285
295
|
Environment variables:
|
|
286
296
|
GLCI_CLICKHOUSE_METRICS_PASSWORD ClickHouse password (required, not passed via CLI for security)
|
data/exe/test-coverage
CHANGED
|
@@ -21,12 +21,12 @@ require_relative '../lib/gitlab_quality/test_tooling/code_coverage/test_report'
|
|
|
21
21
|
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/test_map'
|
|
22
22
|
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/test_file_mapping_data'
|
|
23
23
|
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/per_test_coverage_data'
|
|
24
|
+
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/per_test_coverage_exporter'
|
|
24
25
|
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/source_file_classifier'
|
|
25
26
|
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/responsibility_classifier'
|
|
26
27
|
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/responsibility_patterns_config'
|
|
27
28
|
|
|
28
29
|
params = {}
|
|
29
|
-
required_params = [:test_reports, :coverage_report, :test_map, :clickhouse_url, :clickhouse_database, :clickhouse_username, :clickhouse_shared_database, :responsibility_patterns]
|
|
30
30
|
|
|
31
31
|
options = OptionParser.new do |opts|
|
|
32
32
|
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
|
@@ -68,19 +68,35 @@ options = OptionParser.new do |opts|
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
opts.on('--per-test-coverage GLOB',
|
|
71
|
-
'
|
|
72
|
-
'
|
|
73
|
-
'
|
|
71
|
+
'Per-test coverage mode. Glob for per-test coverage JSON/NDJSON files. ' \
|
|
72
|
+
'Inserts rows into code_coverage.test_coverage_per_file and runs the daily ' \
|
|
73
|
+
'test_health_risk aggregation unless --skip-aggregation. Needs only the ' \
|
|
74
|
+
'--clickhouse-* options; --test-reports and --jest-quarantine-file are ' \
|
|
75
|
+
'optional enrichment. (e.g., "tmp/per-test-coverage-*.ndjson")') do |pattern|
|
|
74
76
|
params[:per_test_coverage] = pattern
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
opts.on('--jest-quarantine-file PATH',
|
|
78
80
|
'Optional. Path to quarantined_vue3_specs.txt. When provided alongside ' \
|
|
79
81
|
'--per-test-coverage, populates code_coverage.jest_quarantined_tests_today ' \
|
|
80
|
-
'before the
|
|
82
|
+
'before the aggregation so Jest tests count toward is_quarantined.') do |path|
|
|
81
83
|
params[:jest_quarantine_file] = path
|
|
82
84
|
end
|
|
83
85
|
|
|
86
|
+
opts.on('--skip-aggregation',
|
|
87
|
+
'Optional. With --per-test-coverage, insert rows (and ingest the jest ' \
|
|
88
|
+
'quarantine file) without running the test_health_risk aggregation. Use ' \
|
|
89
|
+
'when streaming many batches, then run --run-aggregation once at the end.') do
|
|
90
|
+
params[:skip_aggregation] = true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
opts.on('--run-aggregation',
|
|
94
|
+
'Run only the daily test_health_risk aggregation over ' \
|
|
95
|
+
'code_coverage.test_coverage_per_file, then exit. Needs only the ' \
|
|
96
|
+
'--clickhouse-* options.') do
|
|
97
|
+
params[:run_aggregation] = true
|
|
98
|
+
end
|
|
99
|
+
|
|
84
100
|
opts.separator ""
|
|
85
101
|
opts.separator "Environment variables:"
|
|
86
102
|
opts.separator " GLCI_CLICKHOUSE_METRICS_PASSWORD ClickHouse password (required, not passed via CLI for security)"
|
|
@@ -102,31 +118,74 @@ options = OptionParser.new do |opts|
|
|
|
102
118
|
opts.parse(ARGV)
|
|
103
119
|
end
|
|
104
120
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
121
|
+
mode =
|
|
122
|
+
if params[:run_aggregation]
|
|
123
|
+
puts "Warning: --per-test-coverage is ignored in --run-aggregation mode." if params[:per_test_coverage]
|
|
124
|
+
:aggregation
|
|
125
|
+
elsif params[:per_test_coverage]
|
|
126
|
+
:per_test
|
|
127
|
+
else
|
|
128
|
+
:full_export
|
|
110
129
|
end
|
|
111
130
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
# Per-test and aggregation-only modes are self-contained: they touch only the
|
|
132
|
+
# ClickHouse connection, not the LCOV report, Crystalball test map, or
|
|
133
|
+
# responsibility patterns the full coverage-metrics export needs.
|
|
134
|
+
clickhouse_required = [:clickhouse_url, :clickhouse_database, :clickhouse_username]
|
|
135
|
+
full_export_required =
|
|
136
|
+
[:test_reports, :coverage_report, :test_map, :responsibility_patterns, :clickhouse_shared_database] + clickhouse_required
|
|
137
|
+
required_params = mode == :full_export ? full_export_required : clickhouse_required
|
|
118
138
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
139
|
+
unless params.any? && (required_params - params.keys).none?
|
|
140
|
+
puts "Missing argument(s). Required arguments are: #{required_params}\nPassed arguments are: #{params}\n"
|
|
141
|
+
puts options
|
|
142
|
+
exit 1
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
clickhouse_password = ENV.fetch('GLCI_CLICKHOUSE_METRICS_PASSWORD', nil)
|
|
146
|
+
if clickhouse_password.to_s.strip.empty?
|
|
147
|
+
puts "Error: GLCI_CLICKHOUSE_METRICS_PASSWORD environment variable must be set and not empty"
|
|
148
|
+
exit 1
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
non_empty_params = mode == :full_export ? clickhouse_required + [:clickhouse_shared_database] : clickhouse_required
|
|
152
|
+
non_empty_params.each do |param|
|
|
153
|
+
next unless params[param].to_s.strip.empty?
|
|
154
|
+
|
|
155
|
+
puts "Error: --#{param.to_s.tr('_', '-')} cannot be empty"
|
|
156
|
+
exit 1
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
begin
|
|
160
|
+
uri = URI.parse(params[:clickhouse_url])
|
|
161
|
+
unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
|
|
162
|
+
puts "Error: --clickhouse-url must be a valid HTTP or HTTPS URL"
|
|
127
163
|
exit 1
|
|
128
164
|
end
|
|
165
|
+
rescue URI::InvalidURIError
|
|
166
|
+
puts "Error: --clickhouse-url is not a valid URL format"
|
|
167
|
+
exit 1
|
|
168
|
+
end
|
|
129
169
|
|
|
170
|
+
clickhouse_data = {
|
|
171
|
+
url: params[:clickhouse_url],
|
|
172
|
+
database: params[:clickhouse_database],
|
|
173
|
+
username: params[:clickhouse_username],
|
|
174
|
+
password: clickhouse_password
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
case mode
|
|
178
|
+
when :aggregation
|
|
179
|
+
GitlabQuality::TestTooling::CodeCoverage::ClickHouse::TestHealthRiskAggregator.new(**clickhouse_data).run
|
|
180
|
+
when :per_test
|
|
181
|
+
GitlabQuality::TestTooling::CodeCoverage::PerTestCoverageExporter.new(
|
|
182
|
+
coverage_glob: params[:per_test_coverage],
|
|
183
|
+
clickhouse: clickhouse_data,
|
|
184
|
+
test_reports: params[:test_reports],
|
|
185
|
+
jest_quarantine_file: params[:jest_quarantine_file],
|
|
186
|
+
skip_aggregation: params.fetch(:skip_aggregation, false)
|
|
187
|
+
).run
|
|
188
|
+
when :full_export
|
|
130
189
|
artifacts = GitlabQuality::TestTooling::CodeCoverage::Artifacts.new(
|
|
131
190
|
coverage_report: params[:coverage_report],
|
|
132
191
|
test_map: params[:test_map],
|
|
@@ -181,13 +240,6 @@ if params.any? && (required_params - params.keys).none?
|
|
|
181
240
|
test_classifications
|
|
182
241
|
)
|
|
183
242
|
|
|
184
|
-
clickhouse_data = {
|
|
185
|
-
url: params[:clickhouse_url],
|
|
186
|
-
database: params[:clickhouse_database],
|
|
187
|
-
username: params[:clickhouse_username],
|
|
188
|
-
password: clickhouse_password
|
|
189
|
-
}
|
|
190
|
-
|
|
191
243
|
shared_clickhouse_data = {
|
|
192
244
|
url: params[:clickhouse_url],
|
|
193
245
|
database: params[:clickhouse_shared_database],
|
|
@@ -210,36 +262,4 @@ if params.any? && (required_params - params.keys).none?
|
|
|
210
262
|
)
|
|
211
263
|
test_file_mappings_table = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::TestFileMappingsTable.new(**shared_clickhouse_data)
|
|
212
264
|
test_file_mappings_table.push(test_file_mapping_data.as_db_table)
|
|
213
|
-
|
|
214
|
-
# Per-test coverage export (optional). Only runs when --per-test-coverage
|
|
215
|
-
# was provided AND at least one matching artifact exists.
|
|
216
|
-
if params[:per_test_coverage]
|
|
217
|
-
per_test_files = Dir.glob(params[:per_test_coverage])
|
|
218
|
-
if per_test_files.any?
|
|
219
|
-
per_test_data = GitlabQuality::TestTooling::CodeCoverage::PerTestCoverageData.new(
|
|
220
|
-
per_test_files,
|
|
221
|
-
tests_to_categories: tests_to_categories,
|
|
222
|
-
feature_categories_to_teams: category_owners.feature_categories_to_teams,
|
|
223
|
-
captured_sha: ENV.fetch('CI_COMMIT_SHA', '')
|
|
224
|
-
)
|
|
225
|
-
per_test_coverage_table = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::PerTestCoverageTable.new(**clickhouse_data)
|
|
226
|
-
per_test_coverage_table.push(per_test_data.as_db_table)
|
|
227
|
-
|
|
228
|
-
if params[:jest_quarantine_file] && File.exist?(params[:jest_quarantine_file])
|
|
229
|
-
jest_qt = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::JestQuarantinedTestsTable.new(**clickhouse_data)
|
|
230
|
-
jest_qt.populate(quarantine_file: params[:jest_quarantine_file])
|
|
231
|
-
elsif params[:jest_quarantine_file]
|
|
232
|
-
puts "Jest quarantine file not found at #{params[:jest_quarantine_file]}; skipping jest quarantine ingestion."
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
aggregator = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::TestHealthRiskAggregator.new(**clickhouse_data)
|
|
236
|
-
aggregator.run
|
|
237
|
-
else
|
|
238
|
-
puts "No per-test coverage artifacts matched #{params[:per_test_coverage]}; skipping per-test export and aggregation."
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
else
|
|
242
|
-
puts "Missing argument(s). Required arguments are: #{required_params}\nPassed arguments are: #{params}\n"
|
|
243
|
-
puts options
|
|
244
|
-
exit 1
|
|
245
265
|
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
module GitlabQuality
|
|
7
|
+
module TestTooling
|
|
8
|
+
module CodeCoverage
|
|
9
|
+
# Standalone per-test coverage export: inserts per-test, per-source-file
|
|
10
|
+
# rows into `code_coverage.test_coverage_per_file` and, unless skipped,
|
|
11
|
+
# runs the daily `test_health_risk` aggregation.
|
|
12
|
+
#
|
|
13
|
+
# Unlike the full coverage-metrics export, this path needs only ClickHouse
|
|
14
|
+
# credentials and the per-test coverage glob, not the LCOV report, the
|
|
15
|
+
# Crystalball test map, or the responsibility patterns. Category enrichment
|
|
16
|
+
# (feature_category/group/stage/section per row) is optional: pass
|
|
17
|
+
# `test_reports` to map test files to feature categories via the test
|
|
18
|
+
# report JSON, otherwise those columns stay blank.
|
|
19
|
+
#
|
|
20
|
+
# `skip_aggregation` lets a batched caller insert without re-running the
|
|
21
|
+
# table-wide aggregation on every batch. The streaming export invokes this
|
|
22
|
+
# once per artifact batch with `skip_aggregation: true`, then runs the
|
|
23
|
+
# aggregation once at the end.
|
|
24
|
+
class PerTestCoverageExporter
|
|
25
|
+
# @param clickhouse [Hash] connection params (:url, :database, :username, :password)
|
|
26
|
+
def initialize(
|
|
27
|
+
coverage_glob:, clickhouse:,
|
|
28
|
+
test_reports: nil, jest_quarantine_file: nil,
|
|
29
|
+
captured_sha: ENV.fetch('CI_COMMIT_SHA', ''), skip_aggregation: false, logger: nil)
|
|
30
|
+
@coverage_glob = coverage_glob
|
|
31
|
+
@clickhouse = clickhouse
|
|
32
|
+
@test_reports = test_reports
|
|
33
|
+
@jest_quarantine_file = jest_quarantine_file
|
|
34
|
+
@captured_sha = captured_sha.to_s
|
|
35
|
+
@skip_aggregation = skip_aggregation
|
|
36
|
+
@logger = logger || ::Logger.new($stdout)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @return [void]
|
|
40
|
+
def run
|
|
41
|
+
coverage_files = Dir.glob(coverage_glob)
|
|
42
|
+
if coverage_files.empty?
|
|
43
|
+
logger.info(
|
|
44
|
+
"#{ClickHouse::LOG_PREFIX} No per-test coverage artifacts matched #{coverage_glob}; nothing to export."
|
|
45
|
+
)
|
|
46
|
+
return
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
insert(coverage_files)
|
|
50
|
+
ingest_jest_quarantine
|
|
51
|
+
aggregate unless skip_aggregation
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
attr_reader :coverage_glob, :clickhouse, :test_reports, :jest_quarantine_file,
|
|
57
|
+
:captured_sha, :skip_aggregation, :logger
|
|
58
|
+
|
|
59
|
+
def insert(coverage_files)
|
|
60
|
+
tests_to_categories, feature_categories_to_teams = resolve_categories
|
|
61
|
+
|
|
62
|
+
data = PerTestCoverageData.new(
|
|
63
|
+
coverage_files,
|
|
64
|
+
tests_to_categories: tests_to_categories,
|
|
65
|
+
feature_categories_to_teams: feature_categories_to_teams,
|
|
66
|
+
captured_sha: captured_sha
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
ClickHouse::PerTestCoverageTable.new(**clickhouse_credentials).push(data.as_db_table)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def ingest_jest_quarantine
|
|
73
|
+
return unless jest_quarantine_file
|
|
74
|
+
|
|
75
|
+
unless File.exist?(jest_quarantine_file)
|
|
76
|
+
logger.info(
|
|
77
|
+
"#{ClickHouse::LOG_PREFIX} Jest quarantine file not found at #{jest_quarantine_file}; " \
|
|
78
|
+
"skipping jest quarantine ingestion."
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
ClickHouse::JestQuarantinedTestsTable.new(**clickhouse_credentials)
|
|
84
|
+
.populate(quarantine_file: jest_quarantine_file)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def aggregate
|
|
88
|
+
ClickHouse::TestHealthRiskAggregator.new(**clickhouse_credentials).run
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# `tests_to_categories` comes from the test report JSON; without it, rows
|
|
92
|
+
# carry blank categories. `CategoryOwners` fetches stages.yml over HTTP,
|
|
93
|
+
# so only build it when there are reports to enrich.
|
|
94
|
+
def resolve_categories
|
|
95
|
+
return [{}, {}] unless test_reports
|
|
96
|
+
|
|
97
|
+
# Comma-separated patterns, mirroring Artifacts#test_reports.
|
|
98
|
+
patterns = test_reports.split(',').map(&:strip).reject(&:empty?)
|
|
99
|
+
report_files = Dir.glob(patterns)
|
|
100
|
+
return [{}, {}] if report_files.empty?
|
|
101
|
+
|
|
102
|
+
[tests_to_categories_from(report_files), CategoryOwners.new.feature_categories_to_teams]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def tests_to_categories_from(report_files)
|
|
106
|
+
report_files.each_with_object({}) do |file, combined|
|
|
107
|
+
# Category enrichment is optional, so a malformed or vanished report
|
|
108
|
+
# shouldn't abort the per-test insert (the rows are the point). Warn
|
|
109
|
+
# and skip the file, leaving those tests with blank categories.
|
|
110
|
+
begin
|
|
111
|
+
report = TestReport.new(JSON.parse(File.read(file)))
|
|
112
|
+
rescue JSON::ParserError, Errno::ENOENT => e
|
|
113
|
+
logger.warn("#{ClickHouse::LOG_PREFIX} Skipping unreadable test report #{file}: #{e.message}")
|
|
114
|
+
next
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
combined.merge!(report.tests_to_categories) { |_, old_val, new_val| (old_val + new_val).uniq }
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def clickhouse_credentials
|
|
122
|
+
clickhouse.merge(logger: logger)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab_quality-test_tooling
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitLab Quality
|
|
@@ -502,6 +502,7 @@ files:
|
|
|
502
502
|
- lib/gitlab_quality/test_tooling/code_coverage/coverage_data.rb
|
|
503
503
|
- lib/gitlab_quality/test_tooling/code_coverage/lcov_file.rb
|
|
504
504
|
- lib/gitlab_quality/test_tooling/code_coverage/per_test_coverage_data.rb
|
|
505
|
+
- lib/gitlab_quality/test_tooling/code_coverage/per_test_coverage_exporter.rb
|
|
505
506
|
- lib/gitlab_quality/test_tooling/code_coverage/responsibility_classifier.rb
|
|
506
507
|
- lib/gitlab_quality/test_tooling/code_coverage/responsibility_patterns_config.rb
|
|
507
508
|
- lib/gitlab_quality/test_tooling/code_coverage/rspec_report.rb
|