featureparity 0.0.12 → 0.0.13
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/lib/fp/client.rb +5 -1
- data/lib/fp/commands/ci_report.rb +34 -3
- data/lib/fp/junit.rb +20 -0
- data/lib/fp/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e3dba0cc6ba18239f6952822d96bd4526d26d207e8b7e5448554de745e6517a8
|
|
4
|
+
data.tar.gz: 1521147f4b1574062b3e04f22cef0ce1cb7ef6b822f767c3f75ed66a200b6a5c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a65ac140de45599a9a5a27f4705e179fd816bad77fe563477fd6b92efbc0d592cea80f8c90c2e519d559fc604927d6b321c3a3af99bb10c2b99b9e06b39467f5
|
|
7
|
+
data.tar.gz: c9ba4966e9a2d036f266b3646eb0c7569399d606d196175df189047de7693c55badce253c94205045449ca7b20117a6f496eeee486067c03ca2291f2caed026f
|
data/lib/fp/client.rb
CHANGED
|
@@ -151,7 +151,8 @@ module Fp
|
|
|
151
151
|
# server can fall back to resolving markers itself for older clients — but it can
|
|
152
152
|
# only do that by fetching the test files from GitHub, which fails silently on a
|
|
153
153
|
# private repo, so resolved results are always sent when we have them.
|
|
154
|
-
def ingest_ci_results(project_id, ci_url:, surface:, junit_xml:, pr_url: nil, results: nil
|
|
154
|
+
def ingest_ci_results(project_id, ci_url:, surface:, junit_xml:, pr_url: nil, results: nil,
|
|
155
|
+
unmarked_failures: nil)
|
|
155
156
|
body = {
|
|
156
157
|
ci_url: ci_url,
|
|
157
158
|
surface: surface,
|
|
@@ -160,6 +161,9 @@ module Fp
|
|
|
160
161
|
# Only send pr_url when set, so a merged-branch run stays a plain ingest.
|
|
161
162
|
body[:pr_url] = pr_url if pr_url && !pr_url.empty?
|
|
162
163
|
body[:results] = results if results && !results.empty?
|
|
164
|
+
# Loose ends: failing tests with no fp:<slug> marker. The server stores them so the
|
|
165
|
+
# matrix can surface a red suite whose failures aren't bound to any requirement.
|
|
166
|
+
body[:unmarked_failures] = unmarked_failures if unmarked_failures && !unmarked_failures.empty?
|
|
163
167
|
|
|
164
168
|
post("/api/projects/#{project_id}/ci_results", body)
|
|
165
169
|
end
|
|
@@ -82,7 +82,7 @@ module Fp
|
|
|
82
82
|
known = known_slugs(project_id)
|
|
83
83
|
|
|
84
84
|
totals = { created: 0, updated: 0, missing_failing: 0, reports: [], resolved: 0,
|
|
85
|
-
unmatched: 0, unknown: [] }
|
|
85
|
+
unmatched: 0, unknown: [], unmarked_failures: [] }
|
|
86
86
|
|
|
87
87
|
junit_paths.each do |path|
|
|
88
88
|
xml = File.read(path)
|
|
@@ -92,6 +92,7 @@ module Fp
|
|
|
92
92
|
results, unknown = partition_known(resolved[:results], known)
|
|
93
93
|
totals[:resolved] += results.length
|
|
94
94
|
totals[:unknown].concat(unknown)
|
|
95
|
+
totals[:unmarked_failures].concat(resolved[:unmarked_failures])
|
|
95
96
|
|
|
96
97
|
result = client.ingest_ci_results(
|
|
97
98
|
project_id,
|
|
@@ -99,7 +100,8 @@ module Fp
|
|
|
99
100
|
surface: opts[:surface],
|
|
100
101
|
junit_xml: xml,
|
|
101
102
|
pr_url: opts[:pr],
|
|
102
|
-
results: results
|
|
103
|
+
results: results,
|
|
104
|
+
unmarked_failures: resolved[:unmarked_failures]
|
|
103
105
|
)
|
|
104
106
|
|
|
105
107
|
unless result[:ok]
|
|
@@ -154,6 +156,19 @@ module Fp
|
|
|
154
156
|
"#{orphans.join(', ')}. Propose the requirement (fp propose) or fix the slug.")
|
|
155
157
|
end
|
|
156
158
|
|
|
159
|
+
# Loose ends: tests that failed but carry no fp:<slug> marker. Not a gate failure
|
|
160
|
+
# (they bind to no requirement, so nothing can go red), but a red test nobody is
|
|
161
|
+
# tracking is worth a human's eyes — surfaced the same way orphan markers are, and
|
|
162
|
+
# persisted so it shows on the matrix's "loose ends" panel.
|
|
163
|
+
unless totals[:unmarked_failures].empty?
|
|
164
|
+
count = totals[:unmarked_failures].length
|
|
165
|
+
names = totals[:unmarked_failures].map { |f| f[:name] }.compact.take(10).join(', ')
|
|
166
|
+
annotate("#{count} failing test(s) on surface '#{opts[:surface]}' carry no fp:<slug> " \
|
|
167
|
+
"marker, so no requirement tracks them: #{names}#{'…' if count > 10}. " \
|
|
168
|
+
'Mark them (fp:<slug>) to bind them to a requirement, or leave them — ' \
|
|
169
|
+
'they show on the matrix as loose ends either way.')
|
|
170
|
+
end
|
|
171
|
+
|
|
157
172
|
emit_summary(opts, totals)
|
|
158
173
|
end
|
|
159
174
|
|
|
@@ -214,7 +229,18 @@ module Fp
|
|
|
214
229
|
failure_count: r.failure_count
|
|
215
230
|
}
|
|
216
231
|
end,
|
|
217
|
-
unmatched: resolved[:unmatched].length
|
|
232
|
+
unmatched: resolved[:unmatched].length,
|
|
233
|
+
# Loose ends: failing tests with no fp:<slug> marker. Shipped to the server so a
|
|
234
|
+
# human can see them on the matrix; a red test nobody tracks against a
|
|
235
|
+
# requirement is a real gap, even though it isn't a requirement cell.
|
|
236
|
+
unmarked_failures: Fp::JUnit.unmarked_failures(resolved[:unmatched]).map do |f|
|
|
237
|
+
{
|
|
238
|
+
name: f.name,
|
|
239
|
+
classname: f.classname,
|
|
240
|
+
source_file: f.file,
|
|
241
|
+
source_line: f.line
|
|
242
|
+
}
|
|
243
|
+
end
|
|
218
244
|
}
|
|
219
245
|
rescue ArgumentError => e
|
|
220
246
|
output.error(e.message)
|
|
@@ -234,6 +260,7 @@ module Fp
|
|
|
234
260
|
missing_marked_failing: totals[:missing_failing],
|
|
235
261
|
markers_resolved: totals[:resolved],
|
|
236
262
|
unmatched_testcases: totals[:unmatched],
|
|
263
|
+
unmarked_failures: totals[:unmarked_failures],
|
|
237
264
|
skipped_unknown: totals[:unknown].uniq,
|
|
238
265
|
reports: totals[:reports]
|
|
239
266
|
}
|
|
@@ -253,6 +280,10 @@ module Fp
|
|
|
253
280
|
puts " skipped #{totals[:unknown].uniq.length} marker(s) with no matching " \
|
|
254
281
|
"requirement: #{totals[:unknown].uniq.join(', ')}"
|
|
255
282
|
end
|
|
283
|
+
unless totals[:unmarked_failures].empty?
|
|
284
|
+
puts " loose ends: #{totals[:unmarked_failures].length} failing test(s) with no " \
|
|
285
|
+
'fp:<slug> marker (shown on the matrix, not a gate failure)'
|
|
286
|
+
end
|
|
256
287
|
puts " created: #{totals[:created]} updated: #{totals[:updated]} marked failing (missing): #{totals[:missing_failing]}"
|
|
257
288
|
end
|
|
258
289
|
end
|
data/lib/fp/junit.rb
CHANGED
|
@@ -35,6 +35,14 @@ module Fp
|
|
|
35
35
|
CiResult = Struct.new(:slug, :surface, :status, :file, :line, :test_count, :failure_count,
|
|
36
36
|
keyword_init: true)
|
|
37
37
|
|
|
38
|
+
# A failing testcase that carries no fp:<slug> marker at all — a "loose end". Not a
|
|
39
|
+
# cell on the parity grid (nothing binds it to a requirement) and not an orphan
|
|
40
|
+
# marker (there is no marker); it's a red test nobody is tracking against a
|
|
41
|
+
# requirement. CI ships these to the server so a human can see, on the matrix, that
|
|
42
|
+
# the suite has failures no requirement claims. Green unmarked tests are ignored on
|
|
43
|
+
# purpose — not everything needs a requirement; only a *failing* one is a signal.
|
|
44
|
+
UnmarkedFailure = Struct.new(:name, :classname, :file, :line, keyword_init: true)
|
|
45
|
+
|
|
38
46
|
# A testcase paired with the marker that annotates it. `precise` records whether we
|
|
39
47
|
# know *which* test the marker sits above (matched by line or by test name) or only
|
|
40
48
|
# that the marker is somewhere in the same file — a title is only worth reporting in
|
|
@@ -249,6 +257,18 @@ module Fp
|
|
|
249
257
|
{ results: results, unmatched: unmatched, missing_surface: missing_surface.uniq }
|
|
250
258
|
end
|
|
251
259
|
|
|
260
|
+
# The loose ends in a set of unmatched testcases: the ones that *failed* but carry no
|
|
261
|
+
# fp:<slug> marker. Pass the `unmatched` list from `ci_results` (or `bind_markers`).
|
|
262
|
+
#
|
|
263
|
+
# Only failures are returned. A green unmarked test is fine — not everything needs a
|
|
264
|
+
# requirement — but a red one is a failure nobody is tracking, which is exactly the
|
|
265
|
+
# signal a human should see. Skipped tests are not failures, so they're excluded too.
|
|
266
|
+
def unmarked_failures(unmatched)
|
|
267
|
+
unmatched.select(&:failed?).map do |tc|
|
|
268
|
+
UnmarkedFailure.new(name: tc.name, classname: tc.classname, file: tc.file, line: tc.line)
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
252
272
|
# Walk testcases and yield a Match for every one we can tie to a marker.
|
|
253
273
|
# Shared by bind_markers (agent evidence) and ci_results (CI verdicts) so the two
|
|
254
274
|
# can never disagree about which test a marker annotates.
|
data/lib/fp/version.rb
CHANGED