From 72f9ea335179ea2493ca3c951186f7e21e305556 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 00:08:30 +0000 Subject: [PATCH 1/4] fix: add test-coverage workflow and README badges Restore the missing coverage gate with a covr-based GitHub Actions workflow and coverage badges, without weakening existing R CMD check or pkgdown jobs. Co-authored-by: bbuchsbaum --- .github/workflows/test-coverage.yaml | 67 ++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 69 insertions(+) create mode 100644 .github/workflows/test-coverage.yaml diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 0000000..518832c --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,67 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +name: test-coverage + +permissions: + contents: read + id-token: write + +jobs: + test-coverage: + runs-on: ubuntu-latest + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::covr, any::xml2 + needs: coverage + + - name: Test coverage + run: | + cov <- covr::package_coverage( + quiet = FALSE, + clean = FALSE, + install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") + ) + print(cov) + covr::to_cobertura(cov) + shell: Rscript {0} + + - uses: codecov/codecov-action@v4 + with: + # Keep the coverage gate green even if Codecov is not yet linked; + # coverage is still computed and reported in the job logs above. + fail_ci_if_error: false + file: ./cobertura.xml + plugin: noop + disable_search: true + use_oidc: true + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Show testthat output + if: always() + run: | + ## -------------------------------------------------------------------- + find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload test results + if: failure() + uses: actions/upload-artifact@v4 + with: + name: coverage-test-failures + path: ${{ runner.temp }}/package diff --git a/README.md b/README.md index 268e5c1..d858a4b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![R-CMD-check](https://github.com/bbuchsbaum/templateflow/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/bbuchsbaum/templateflow/actions/workflows/R-CMD-check.yaml) [![pkgdown](https://github.com/bbuchsbaum/templateflow/actions/workflows/pkgdown.yaml/badge.svg)](https://bbuchsbaum.github.io/templateflow/) +[![test-coverage](https://github.com/bbuchsbaum/templateflow/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/bbuchsbaum/templateflow/actions/workflows/test-coverage.yaml) +[![Codecov test coverage](https://codecov.io/gh/bbuchsbaum/templateflow/branch/main/graph/badge.svg)](https://app.codecov.io/gh/bbuchsbaum/templateflow?branch=main) R client for the [TemplateFlow](https://www.templateflow.org/) archive of neuroimaging templates. From 089722627675dca590a9022e0016aab73c931630 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 00:19:04 +0000 Subject: [PATCH 2/4] fix: align test-coverage workflow with portfolio Codecov template Use checkout@v6, pinned codecov-action v7, upload-artifact@v7, plural Codecov inputs, and strict OIDC upload (fail_ci_if_error) without a token. Report overall covr percent in the job log. Co-authored-by: bbuchsbaum --- .github/workflows/test-coverage.yaml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 518832c..60447cd 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -19,7 +19,7 @@ jobs: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: r-lib/actions/setup-r@v2 with: @@ -38,19 +38,23 @@ jobs: install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") ) print(cov) + pct <- round(covr::percent_coverage(cov), 2) + cat(sprintf("Overall coverage: %s%%\n", pct)) + writeLines(as.character(pct), "coverage-percent.txt") covr::to_cobertura(cov) shell: Rscript {0} - - uses: codecov/codecov-action@v4 + - name: Report coverage percent + run: | + echo "Overall package coverage: $(cat coverage-percent.txt)%" + + - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7 with: - # Keep the coverage gate green even if Codecov is not yet linked; - # coverage is still computed and reported in the job logs above. - fail_ci_if_error: false - file: ./cobertura.xml - plugin: noop + fail_ci_if_error: true + files: ./cobertura.xml + plugins: noop disable_search: true use_oidc: true - token: ${{ secrets.CODECOV_TOKEN }} - name: Show testthat output if: always() @@ -61,7 +65,7 @@ jobs: - name: Upload test results if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: coverage-test-failures path: ${{ runner.temp }}/package From 7b69edcbbac4700ad5c0e6fc2bb1a4b52a729498 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 00:27:26 +0000 Subject: [PATCH 3/4] test: cover download retries and valuable CLI branches Add mocked HTTPS download tests for atomic write, HTTP/transport failures, retry-then-success, and DataLad-to-S3 fallback, plus CLI coverage for get/cite/meta/doctor/autoupdate without live network I/O. Co-authored-by: bbuchsbaum --- tests/testthat/test_cli_coverage.R | 123 ++++++++++++++++++ tests/testthat/test_download.R | 198 +++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 tests/testthat/test_cli_coverage.R create mode 100644 tests/testthat/test_download.R diff --git a/tests/testthat/test_cli_coverage.R b/tests/testthat/test_cli_coverage.R new file mode 100644 index 0000000..d161ade --- /dev/null +++ b/tests/testthat/test_cli_coverage.R @@ -0,0 +1,123 @@ +# Additional CLI branches not covered by test_cli*.R -------------------------- + +test_that("tf_cli get lists matching files without downloading present assets", { + tmp <- file.path(tempdir(), "tf-cli-get-branch") + if (dir.exists(tmp)) unlink(tmp, recursive = TRUE, force = TRUE) + on.exit(unlink(tmp, recursive = TRUE, force = TRUE), add = TRUE) + + # Seed a real non-empty JSON asset so get short-circuits before network I/O. + tpl_dir <- file.path(tmp, "tpl-TestTpl") + dir.create(tpl_dir, recursive = TRUE) + asset <- file.path(tpl_dir, "tpl-TestTpl_desc-ref_template_description.json") + writeLines('{"Name":"TestTpl"}', asset) + + withr::with_options( + list(templateflow.test.forcebundled = TRUE), + { + cfg <- tf_default_config(root = tmp) + cache <- TemplateFlowCache(config = cfg) + cache$layout <- tf_build_layout(tmp) + client <- structure(list(cache = cache), class = "TemplateFlowClient") + + testthat::local_mocked_bindings( + tf_cli_make_client = function(root, autoupdate) client, + .package = "templateflow" + ) + + output <- capture.output({ + paths <- tf_cli(c("--root", tmp, "get", "TestTpl", "--suffix", "template_description")) + }) + expect_true(length(paths) >= 1) + expect_true(any(grepl("template_description", paths)) || + any(grepl("template_description", output))) + } + ) +}) + +test_that("tf_cli get errors without a template argument", { + expect_error(tf_cli(c("get")), class = "tf_cli_error") +}) + +test_that("tf_cli cite and cite --bibtex print citations", { + tmp <- file.path(tempdir(), "tf-cli-cite-branch") + if (dir.exists(tmp)) unlink(tmp, recursive = TRUE, force = TRUE) + on.exit(unlink(tmp, recursive = TRUE, force = TRUE), add = TRUE) + + out_plain <- capture.output(tf_cli(c("--root", tmp, "cite", "MNI152Lin"))) + expect_true(length(out_plain) >= 1) + + # Keep --bibtex offline: stub DOI conversion at the package boundary. + testthat::local_mocked_bindings( + tf_to_bibtex = function(doi, timeout) paste0("@article{stub,\n url = {", doi, "}\n}"), + .package = "templateflow" + ) + out_bib <- capture.output(tf_cli(c("--root", tmp, "cite", "MNI152Lin", "--bibtex"))) + expect_true(any(grepl("@article|doi|MNI|template|http", out_bib, ignore.case = TRUE))) +}) + +test_that("tf_cli cite errors without a template argument", { + expect_error(tf_cli(c("cite")), class = "tf_cli_error") +}) + +test_that("tf_cli meta errors for unknown fields and missing template", { + tmp <- file.path(tempdir(), "tf-cli-meta-branch") + if (dir.exists(tmp)) unlink(tmp, recursive = TRUE, force = TRUE) + on.exit(unlink(tmp, recursive = TRUE, force = TRUE), add = TRUE) + + expect_error(tf_cli(c("meta")), class = "tf_cli_error") + expect_error( + tf_cli(c("--root", tmp, "meta", "MNI152Lin", "--field", "DefinitelyMissing")), + class = "tf_cli_error" + ) +}) + +test_that("tf_cli doctor --fix removes zero-byte and XML error files", { + tmp <- file.path(tempdir(), "tf-cli-doctor-fix") + if (dir.exists(tmp)) unlink(tmp, recursive = TRUE, force = TRUE) + on.exit(unlink(tmp, recursive = TRUE, force = TRUE), add = TRUE) + + client <- TemplateFlowClient(root = tmp) + zero <- file.path(tmp, "tpl-MNI152Lin", "zero-stub.nii.gz") + dir.create(dirname(zero), recursive = TRUE, showWarnings = FALSE) + file.create(zero) + + xml <- file.path(tmp, "tpl-MNI152Lin", "bad.xml") + file.copy(testthat::test_path("data", "error_response.xml"), xml, overwrite = TRUE) + + output <- capture.output(tf_cli(c("--root", tmp, "doctor", "--fix"))) + expect_true(any(grepl("Removed|zero-byte|XML", output, ignore.case = TRUE))) + expect_false(file.exists(zero)) + expect_false(file.exists(xml)) +}) + +test_that("tf_cli parses --autoupdate and rejects invalid values", { + tmp <- file.path(tempdir(), "tf-cli-autoupdate") + if (dir.exists(tmp)) unlink(tmp, recursive = TRUE, force = TRUE) + on.exit(unlink(tmp, recursive = TRUE, force = TRUE), add = TRUE) + + expect_output( + tf_cli(c("--root", tmp, "--autoupdate", "off", "config")), + "TEMPLATEFLOW_AUTOUPDATE=off" + ) + expect_error( + tf_cli(c("--root", tmp, "--autoupdate", "maybe", "config")), + class = "tf_cli_error" + ) + expect_error(tf_cli(c("--root")), class = "tf_cli_error") + expect_error(tf_cli(c("--autoupdate")), class = "tf_cli_error") +}) + +test_that("tf_cli update --help and refresh --help print usage", { + expect_true(any(grepl("Usage:", capture.output(tf_cli(c("update", "--help")))))) + expect_true(any(grepl("Usage:", capture.output(tf_cli(c("refresh", "--help")))))) + expect_true(any(grepl("Usage:", capture.output(tf_cli(c("doctor", "--help")))))) + expect_true(any(grepl("Usage:", capture.output(tf_cli(c("cite", "--help")))))) + expect_true(any(grepl("Usage:", capture.output(tf_cli(c("get", "--help")))))) +}) + +test_that("tf_cli flag without value errors", { + expect_error( + tf_cli(c("--root", tempdir(), "ls", "MNI152Lin", "--suffix")), + class = "tf_cli_error" + ) +}) diff --git a/tests/testthat/test_download.R b/tests/testthat/test_download.R new file mode 100644 index 0000000..7db077e --- /dev/null +++ b/tests/testthat/test_download.R @@ -0,0 +1,198 @@ +# Download helpers: error / retry / atomic-write with mocked HTTP -------------- + +local_force_https_fallback <- function(env = parent.frame()) { + if (!requireNamespace("aws.s3", quietly = TRUE)) { + return(invisible(NULL)) + } + # Prefer the HTTPS path under test; do not hit S3 even if Suggests are present. + testthat::local_mocked_bindings( + save_object = function(...) stop("forced aws.s3 fallback for tests", call. = FALSE), + .package = "aws.s3", + .env = env + ) +} + +local_mock_req_perform <- function(handler, env = parent.frame()) { + testthat::local_mocked_bindings( + req_perform = handler, + .package = "httr2", + .env = env + ) +} + +test_that("tf_s3_url builds HTTPS object URLs from cache-relative paths", { + root <- tempfile("tf-s3-url-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, s3_root = "https://example.test/tpl") + fp <- file.path(root, "tpl-Demo", "tpl-Demo_T1w.nii.gz") + expect_equal( + tf_s3_url(cfg, fp), + "https://example.test/tpl/tpl-Demo/tpl-Demo_T1w.nii.gz" + ) +}) + +test_that("tf_download_file atomically writes on mocked HTTPS success", { + local_force_https_fallback() + root <- tempfile("tf-dl-ok-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, timeout = 1) + fp <- file.path(root, "tpl-Demo", "payload.bin") + payload <- charToRaw("templateflow-bytes") + + local_mock_req_perform(function(req, path = NULL, ...) { + expect_true(grepl("tpl-Demo/payload.bin$", req$url)) + if (!is.null(path)) writeBin(payload, path) + httr2::response(status_code = 200L, url = req$url, body = payload) + }) + + out <- suppressMessages(tf_download_file(cfg, fp, max_retries = 1)) + expect_identical(out, fp) + expect_true(file.exists(fp)) + expect_equal(readBin(fp, what = "raw", n = length(payload)), payload) + leftovers <- list.files(dirname(fp), full.names = TRUE) + expect_identical(normalizePath(leftovers), normalizePath(fp)) +}) + +test_that("tf_download_file retries HTTP errors then succeeds", { + local_force_https_fallback() + root <- tempfile("tf-dl-retry-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, timeout = 1) + fp <- file.path(root, "tpl-Demo", "retry.bin") + payload <- charToRaw("after-retry") + attempts <- 0L + + local_mock_req_perform(function(req, path = NULL, ...) { + attempts <<- attempts + 1L + if (attempts < 2L) { + return(httr2::response(status_code = 503L, url = req$url, body = charToRaw("busy"))) + } + if (!is.null(path)) writeBin(payload, path) + httr2::response(status_code = 200L, url = req$url, body = payload) + }) + + # One backoff sleep (attempt 1 -> 2) is intentional to exercise the retry path. + out <- suppressMessages(tf_download_file(cfg, fp, max_retries = 2)) + expect_identical(out, fp) + expect_equal(attempts, 2L) + expect_equal(readBin(fp, what = "raw", n = length(payload)), payload) +}) + +test_that("tf_download_file aborts after exhausting HTTP error retries", { + local_force_https_fallback() + root <- tempfile("tf-dl-http-fail-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, timeout = 1) + fp <- file.path(root, "tpl-Demo", "missing.bin") + attempts <- 0L + + local_mock_req_perform(function(req, path = NULL, ...) { + attempts <<- attempts + 1L + httr2::response(status_code = 404L, url = req$url, body = charToRaw("missing")) + }) + + # max_retries = 1 avoids backoff sleep while still covering the HTTP failure abort. + expect_error( + suppressMessages(tf_download_file(cfg, fp, max_retries = 1)), + class = "templateflow_network_error" + ) + expect_equal(attempts, 1L) + expect_false(file.exists(fp)) +}) + +test_that("tf_download_file aborts after transport errors", { + local_force_https_fallback() + root <- tempfile("tf-dl-net-fail-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, timeout = 1) + fp <- file.path(root, "tpl-Demo", "offline.bin") + attempts <- 0L + + local_mock_req_perform(function(req, path = NULL, ...) { + attempts <<- attempts + 1L + stop("connection refused", call. = FALSE) + }) + + expect_error( + suppressMessages(tf_download_file(cfg, fp, max_retries = 1)), + class = "templateflow_network_error" + ) + expect_equal(attempts, 1L) + expect_false(file.exists(fp)) +}) + +test_that("tf_fetch_files no-ops for empty or already-present files", { + root <- tempfile("tf-fetch-noop-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root) + cache <- TemplateFlowCache(config = cfg) + + expect_true(tf_fetch_files(cache, character())) + + present <- file.path(root, "already.bin") + writeBin(charToRaw("cached"), present) + expect_true(tf_fetch_files(cache, present)) +}) + +test_that("tf_fetch_files downloads missing zero-byte stubs via mocked HTTPS", { + local_force_https_fallback() + root <- tempfile("tf-fetch-missing-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, timeout = 1, use_datalad = FALSE) + cache <- TemplateFlowCache(config = cfg) + + missing <- c( + file.path(root, "tpl-Demo", "a.bin"), + file.path(root, "tpl-Demo", "b.bin") + ) + dir.create(dirname(missing[[1]]), recursive = TRUE, showWarnings = FALSE) + file.create(missing) + + local_mock_req_perform(function(req, path = NULL, ...) { + if (!is.null(path)) writeBin(charToRaw("filled"), path) + httr2::response(status_code = 200L, url = req$url, body = charToRaw("filled")) + }) + + expect_true(suppressMessages(tf_fetch_files(cache, missing))) + expect_true(all(file.info(missing)$size > 0)) +}) + +test_that("tf_fetch_files falls back to S3 when DataLad get fails", { + local_force_https_fallback() + root <- tempfile("tf-fetch-datalad-") + dir.create(root) + on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + + cfg <- tf_default_config(root = root, timeout = 1, use_datalad = TRUE) + cache <- TemplateFlowCache(config = cfg) + missing <- file.path(root, "tpl-Demo", "dl.bin") + dir.create(dirname(missing), recursive = TRUE, showWarnings = FALSE) + file.create(missing) + + testthat::local_mocked_bindings( + tf_datalad_available = function() TRUE, + tf_datalad_get = function(...) stop("datalad unavailable in test", call. = FALSE), + .package = "templateflow" + ) + local_mock_req_perform(function(req, path = NULL, ...) { + if (!is.null(path)) writeBin(charToRaw("via-s3"), path) + httr2::response(status_code = 200L, url = req$url, body = charToRaw("via-s3")) + }) + + expect_true(suppressMessages(tf_fetch_files(cache, missing))) + expect_equal(rawToChar(readBin(missing, what = "raw", n = 16)), "via-s3") +}) From bb979e164e796fefc2e7ea94b8299ce8c6c5b24a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 28 Aug 2026 00:35:30 +0000 Subject: [PATCH 4/4] test: build download fixtures from canonical cfg$root Avoid macOS /var vs /private/var tempfile alias mismatches by deriving every cache filepath from tf_default_config()$root after normalization. Co-authored-by: bbuchsbaum --- tests/testthat/test_download.R | 85 ++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/tests/testthat/test_download.R b/tests/testthat/test_download.R index 7db077e..bd730b9 100644 --- a/tests/testthat/test_download.R +++ b/tests/testthat/test_download.R @@ -20,13 +20,23 @@ local_mock_req_perform <- function(handler, env = parent.frame()) { ) } +# Build cache-relative paths from the canonical config root. On macOS, +# tempfile() may yield /var/folders/... while tf_default_config() stores +# normalizePath() → /private/var/folders/...; tf_s3_url()/tf_relpath() require +# the filepath to live under cfg$root. +local_cfg_root <- function(prefix, ...) { + staging <- tempfile(prefix) + dir.create(staging) + cfg <- tf_default_config(root = staging, ...) + list(cfg = cfg, root = cfg$root) +} + test_that("tf_s3_url builds HTTPS object URLs from cache-relative paths", { - root <- tempfile("tf-s3-url-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-s3-url-", s3_root = "https://example.test/tpl") + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, s3_root = "https://example.test/tpl") - fp <- file.path(root, "tpl-Demo", "tpl-Demo_T1w.nii.gz") + fp <- file.path(cfg$root, "tpl-Demo", "tpl-Demo_T1w.nii.gz") expect_equal( tf_s3_url(cfg, fp), "https://example.test/tpl/tpl-Demo/tpl-Demo_T1w.nii.gz" @@ -35,12 +45,11 @@ test_that("tf_s3_url builds HTTPS object URLs from cache-relative paths", { test_that("tf_download_file atomically writes on mocked HTTPS success", { local_force_https_fallback() - root <- tempfile("tf-dl-ok-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-dl-ok-", timeout = 1) + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, timeout = 1) - fp <- file.path(root, "tpl-Demo", "payload.bin") + fp <- file.path(cfg$root, "tpl-Demo", "payload.bin") payload <- charToRaw("templateflow-bytes") local_mock_req_perform(function(req, path = NULL, ...) { @@ -59,12 +68,11 @@ test_that("tf_download_file atomically writes on mocked HTTPS success", { test_that("tf_download_file retries HTTP errors then succeeds", { local_force_https_fallback() - root <- tempfile("tf-dl-retry-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-dl-retry-", timeout = 1) + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, timeout = 1) - fp <- file.path(root, "tpl-Demo", "retry.bin") + fp <- file.path(cfg$root, "tpl-Demo", "retry.bin") payload <- charToRaw("after-retry") attempts <- 0L @@ -86,12 +94,11 @@ test_that("tf_download_file retries HTTP errors then succeeds", { test_that("tf_download_file aborts after exhausting HTTP error retries", { local_force_https_fallback() - root <- tempfile("tf-dl-http-fail-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-dl-http-fail-", timeout = 1) + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, timeout = 1) - fp <- file.path(root, "tpl-Demo", "missing.bin") + fp <- file.path(cfg$root, "tpl-Demo", "missing.bin") attempts <- 0L local_mock_req_perform(function(req, path = NULL, ...) { @@ -110,12 +117,11 @@ test_that("tf_download_file aborts after exhausting HTTP error retries", { test_that("tf_download_file aborts after transport errors", { local_force_https_fallback() - root <- tempfile("tf-dl-net-fail-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-dl-net-fail-", timeout = 1) + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, timeout = 1) - fp <- file.path(root, "tpl-Demo", "offline.bin") + fp <- file.path(cfg$root, "tpl-Demo", "offline.bin") attempts <- 0L local_mock_req_perform(function(req, path = NULL, ...) { @@ -132,32 +138,30 @@ test_that("tf_download_file aborts after transport errors", { }) test_that("tf_fetch_files no-ops for empty or already-present files", { - root <- tempfile("tf-fetch-noop-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-fetch-noop-") + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root) cache <- TemplateFlowCache(config = cfg) expect_true(tf_fetch_files(cache, character())) - present <- file.path(root, "already.bin") + present <- file.path(cfg$root, "already.bin") writeBin(charToRaw("cached"), present) expect_true(tf_fetch_files(cache, present)) }) test_that("tf_fetch_files downloads missing zero-byte stubs via mocked HTTPS", { local_force_https_fallback() - root <- tempfile("tf-fetch-missing-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-fetch-missing-", timeout = 1, use_datalad = FALSE) + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, timeout = 1, use_datalad = FALSE) cache <- TemplateFlowCache(config = cfg) missing <- c( - file.path(root, "tpl-Demo", "a.bin"), - file.path(root, "tpl-Demo", "b.bin") + file.path(cfg$root, "tpl-Demo", "a.bin"), + file.path(cfg$root, "tpl-Demo", "b.bin") ) dir.create(dirname(missing[[1]]), recursive = TRUE, showWarnings = FALSE) file.create(missing) @@ -173,13 +177,12 @@ test_that("tf_fetch_files downloads missing zero-byte stubs via mocked HTTPS", { test_that("tf_fetch_files falls back to S3 when DataLad get fails", { local_force_https_fallback() - root <- tempfile("tf-fetch-datalad-") - dir.create(root) - on.exit(unlink(root, recursive = TRUE, force = TRUE), add = TRUE) + setup <- local_cfg_root("tf-fetch-datalad-", timeout = 1, use_datalad = TRUE) + cfg <- setup$cfg + on.exit(unlink(setup$root, recursive = TRUE, force = TRUE), add = TRUE) - cfg <- tf_default_config(root = root, timeout = 1, use_datalad = TRUE) cache <- TemplateFlowCache(config = cfg) - missing <- file.path(root, "tpl-Demo", "dl.bin") + missing <- file.path(cfg$root, "tpl-Demo", "dl.bin") dir.create(dirname(missing), recursive = TRUE, showWarnings = FALSE) file.create(missing)