source(file.path("..", "failing-builds-classify.R")) test_that("normalise_error collapses temp paths, versions, and package tokens", { a <- normalise_error( "In file /tmp/RtmpAb12/foo.c: RcppParallel 5.1.9 failed at 0xdeadbeef", package = "RcppParallel" ) expect_false(grepl("RtmpAb12", a)) expect_false(grepl("5\\.1\\.9", a)) expect_false(grepl("0xdeadbeef", a)) expect_true(grepl("", a)) expect_true(grepl("", a)) # Same root error across two versions collapses to one fingerprint. e1 <- "StanHeaders 2.32.1: tbb/tbb_stddef.h: No such file or directory" e2 <- "StanHeaders 2.33.0: tbb/tbb_stddef.h: No such file or directory" expect_identical( fingerprint_error(e1, "StanHeaders"), fingerprint_error(e2, "StanHeaders") ) }) test_that("normalise_error is safe on NA/empty input", { expect_identical(normalise_error(NA_character_), "") expect_identical(normalise_error(""), "") expect_identical(fingerprint_error(NA_character_), "") }) test_that("fingerprint_error picks the salient error line, not the last line", { txt <- paste( "* installing *source* package 'foo' ...", "error: bar.h: No such file or directory", "* removing '/tmp/lib/foo'", sep = "\n" ) fp <- fingerprint_error(txt, "foo") expect_true(grepl("no such file", fp)) expect_false(grepl("removing", fp)) }) test_that("classify_error matches the removed TBB header signature (makevars, auto)", { sig <- classify_error( "fatal error: tbb/tbb_stddef.h: No such file or directory" ) expect_identical(sig$id, "tbb-stddef-removed") expect_identical(sig$tier, "makevars") expect_true(sig$auto) expect_true(sig$matched) }) test_that("classify_error matches RcppParallel bundled-TBB failures", { sig <- classify_error( "Error: USE_TBB=Linux is not supported on this toolchain" ) expect_identical(sig$id, "rcppparallel-bundled-tbb") expect_identical(sig$tier, "patch") expect_true(sig$auto) }) test_that("system libuv link leak is classified but stays human-only (novel diff)", { sig <- classify_error("cannot open shared object file: libuv.so.1") expect_identical(sig$id, "system-libuv-link-leak") expect_true(sig$matched) expect_false(sig$auto) }) test_that("unknown signatures are never guessed at", { sig <- classify_error("segfault: memory not mapped at address") expect_identical(sig$id, "unclassified") expect_false(sig$matched) expect_false(sig$auto) expect_true(is.na(sig$tier)) }) test_that("propose_registry_entry fills a schema-valid entry for a known lever", { sig <- classify_error("tbb/tbb_stddef.h: No such file") json <- propose_registry_entry(sig, "StanHeaders", c("alpine", "ubuntu-2604")) entry <- jsonlite::fromJSON(json, simplifyVector = FALSE) expect_identical(entry$package, "StanHeaders") expect_identical(entry$versions, "*") expect_identical(entry$platforms, list("alpine", "ubuntu-2604")) expect_identical(entry$makevars$CPPFLAGS, "-DTBB_INTERFACE_NEW") expect_true(nzchar(entry$reason)) # No template -> no proposal (unclassified path). expect_null(propose_registry_entry(classify_error("weird"), "x", "alpine")) })