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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b98c13a1f3911211fc2d07f23ad5917f6d6326563abda2b31bdb52840750f26
4
- data.tar.gz: ec5cb53697d89763e41735deea90ba9342905b5622d5d3c43187782bef3205b5
3
+ metadata.gz: e3dba0cc6ba18239f6952822d96bd4526d26d207e8b7e5448554de745e6517a8
4
+ data.tar.gz: 1521147f4b1574062b3e04f22cef0ce1cb7ef6b822f767c3f75ed66a200b6a5c
5
5
  SHA512:
6
- metadata.gz: 404d64805e0a04b939f920ca3ecd5fbc6765ad4f0e11e62f396ad3794eea8b4c500168a474f06f6df914d088a517a735b0cc801b3bfb73b2733722ac0e6eae0e
7
- data.tar.gz: a80c7661ab55355e9d9ab13ab2430ea2b83d52c41f3185793cf4a1b39671507f5d4f3227d1540d5be305194ad42ea99c0292e651283562b7ac71487d0baf5768
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fp
4
- VERSION = '0.0.12'
4
+ VERSION = '0.0.13'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: featureparity
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla