build-cran-binaries/local/tests/test-proposal-tracking-lib.R
pat-s 8afa584da3
feat(local): auto-propose registry patches and track the feedback loop
Implement steps 3 + 4 of issue #115 on top of the failure classifier, now
that bincraft v4.4.3 applies registry patches/makevars/configure_args to the
target build (not just dependencies), so a trial patched build is meaningful.

- refactor the classify helpers to expose a pure build_triage_report() and a
  list-returning entry builder; failing-builds-report.R now renders from it
- add local/propose-patches.R (step 3, "propose, do not apply"): emit a
  pre-filled registry.json entry for each classified, safe, unregistered
  failure, validate the candidate set against a temporary merged registry,
  and (only on request) --write it plus a proposals ledger, or --open-issue a
  Forgejo tracking issue; the human gate and validator/trial-build acceptance
  stay, and novel source diffs / unknown signatures are never proposed
- add local/trial-build-patch.R: isolated bincraft build of one package with
  the registry applied (no upload/archive/metadata) as the pre-merge gate
- add local/proposal-tracking.R + local/proposal-tracking-lib.R (step 4):
  signature hit rate, proposed-vs-merged, and retirement candidates, with the
  pure helpers covered by tests
- teach validate-patches.R optional PATCH_DIR/REGISTRY_FILE overrides so a
  candidate registry can be validated without touching the real one
- document the propose/trial-build/tracking workflow in local/patches/README.md
2026-07-14 15:01:55 +00:00

105 lines
3.3 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("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
)
})