build-cran-binaries/local/tests/test-proposal-tracking-lib.R
pat-s ec64fb6db8
fix(local): route multi-signature packages to human triage in the proposer
A package whose failing builds match more than one auto-proposable signature
(e.g. some logs hit `tbb-stddef-removed` and others `rcppparallel-bundled-tbb`)
produced two candidate registry entries with the same package/platforms/versions
key, so the candidate set failed `validate-patches.R` with "ambiguous duplicate
entries" and the whole proposer run aborted.

Add a pure `dedupe_candidates()` that keeps a package only when it maps to a
single signature and routes genuinely ambiguous packages (conflicting fix tiers)
to human triage instead of guessing between them. The proposer prints the
skipped packages and proceeds with the unambiguous candidates.
2026-07-14 17:11:02 +00:00

137 lines
4.5 KiB
R

source(file.path("..", "proposal-tracking-lib.R"))
source(file.path("..", "failing-builds-classify.R"))
mk_failures <- function() {
data.frame(
name = c("StanHeaders", "rstan", "RcppParallel", "somepkg"),
platform = c("alpine-321", "alpine-321", "alpine-320", "redhat-9"),
arch = c("amd64", "arm64", "amd64", "amd64"),
error_text = c(
"fatal error: tbb/tbb_stddef.h: No such file or directory",
"In file: tbb/tbb_stddef.h: No such file or directory",
"Error: USE_TBB=Linux is not supported; bundled TBB on musl",
"some unmatched failure"
),
stringsAsFactors = FALSE
)
}
test_that("merge_ledger appends new proposals and preserves existing history", {
existing <- list(list(
package = "fs",
signature = "system-libuv-link-leak",
status = "merged"
))
new <- list(
list(
package = "fs",
signature = "system-libuv-link-leak",
status = "proposed"
),
list(
package = "StanHeaders",
signature = "tbb-stddef-removed",
status = "proposed"
)
)
merged <- merge_ledger(existing, new)
expect_length(merged, 2L) # fs is deduped, StanHeaders added
fs <- Filter(function(r) r$package == "fs", merged)[[1L]]
expect_identical(fs$status, "merged") # existing status preserved, not clobbered
})
test_that("merge_ledger handles an empty/NULL starting ledger", {
new <- list(list(package = "x", signature = "s"))
expect_length(merge_ledger(NULL, new), 1L)
expect_length(merge_ledger(list(), new), 1L)
})
test_that("dedupe_candidates keeps single-signature pkgs, routes conflicts to triage", {
candidates <- list(
list(package = "StanHeaders", signature = "tbb-stddef-removed"),
list(package = "hmmTMB", signature = "tbb-stddef-removed"),
list(package = "hmmTMB", signature = "rcppparallel-bundled-tbb") # conflict
)
out <- dedupe_candidates(candidates)
kept <- vapply(out$keep, function(c) c$package, character(1L))
expect_identical(sort(kept), "StanHeaders") # hmmTMB dropped as ambiguous
expect_true("hmmTMB" %in% names(out$ambiguous))
expect_setequal(
out$ambiguous$hmmTMB,
c("tbb-stddef-removed", "rcppparallel-bundled-tbb")
)
})
test_that("dedupe_candidates collapses a package repeated under one signature", {
candidates <- list(
list(package = "rstan", signature = "tbb-stddef-removed"),
list(package = "rstan", signature = "tbb-stddef-removed")
)
out <- dedupe_candidates(candidates)
expect_length(out$keep, 1L)
expect_length(out$ambiguous, 0L)
})
test_that("dedupe_candidates handles the empty list", {
out <- dedupe_candidates(list())
expect_length(out$keep, 0L)
expect_length(out$ambiguous, 0L)
})
test_that("signature_hit_rate splits addressed vs open per signature", {
report <- build_triage_report(mk_failures(), registered_pkgs = "RcppParallel")
hit <- signature_hit_rate(report, registered_pkgs = "RcppParallel")
tbb <- Filter(function(h) h$signature == "tbb-stddef-removed", hit)[[1L]]
expect_identical(tbb$packages, 2L) # StanHeaders + rstan
expect_identical(tbb$addressed, 0L)
expect_identical(tbb$open, 2L)
expect_true(tbb$auto_proposable)
rcpp <- Filter(function(h) h$signature == "rcppparallel-bundled-tbb", hit)[[
1L
]]
expect_identical(rcpp$addressed, 1L) # already registered
expect_identical(rcpp$open, 0L)
# Unclassified failures never appear as a signature.
expect_false(
"unclassified" %in% vapply(hit, function(h) h$signature, character(1L))
)
})
test_that("proposed_vs_merged marks a package merged once it is registered", {
ledger <- list(
list(
package = "StanHeaders",
signature = "tbb-stddef-removed",
status = "proposed"
),
list(
package = "rstan",
signature = "tbb-stddef-removed",
status = "proposed"
)
)
pvm <- proposed_vs_merged(ledger, registered_pkgs = "StanHeaders")
expect_identical(pvm$total, 2L)
expect_identical(pvm$merged, 1L)
stan <- Filter(function(r) r$package == "StanHeaders", pvm$records)[[1L]]
expect_identical(stan$status, "merged")
})
test_that("retirement_candidates flags entries whose package no longer fails", {
entries <- list(
list(package = "RcppParallel"),
list(package = "oldpkg")
)
# RcppParallel still fails; oldpkg does not -> only oldpkg is retirable.
out <- retirement_candidates(
entries,
failing_pkgs = c("RcppParallel", "StanHeaders")
)
expect_identical(out, "oldpkg")
expect_length(
retirement_candidates(entries, failing_pkgs = c("RcppParallel", "oldpkg")),
0L
)
})