diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 30b26574..00000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 0ebf3385..2d236452 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -9,7 +9,9 @@ on: name: test-coverage.yaml -permissions: read-all +permissions: + contents: read + id-token: write jobs: test-coverage: @@ -39,10 +41,9 @@ jobs: covr::to_cobertura(cov) shell: Rscript {0} - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v5 with: + use_oidc: true fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }} - file: ./cobertura.xml - plugin: noop + files: ./cobertura.xml disable_search: true - token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4ca58309..0a25af48 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .Rhistory .RData .Ruserdata +.DS_Store old repomix.* Rplots.pdf diff --git a/NEWS.md b/NEWS.md index fb539b4b..1703b9fc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ # bidser 0.5.0 +* Allow `create_mock_bids()` to construct an empty project when + `file_structure` has the required columns but zero rows. +* Make the quickstart vignette exit cleanly when its example dataset cannot be + downloaded, instead of passing a missing path to `bids_project()`. + * `read_sidecar(inherit = TRUE)` now anchors on the imaging scans matching the query and resolves each scan's effective metadata via BIDS inheritance, returning one row per scan. Previously it anchored on JSON sidecar *files*, so diff --git a/R/all_generic.R b/R/all_generic.R index 12c00f91..7aca7c30 100644 --- a/R/all_generic.R +++ b/R/all_generic.R @@ -968,6 +968,17 @@ read_dataset_description <- function(x, ...) { #' @param ... Additional arguments passed to methods. #' @return A character scalar. #' @export +#' @examples +#' desc_dir <- tempfile("bids-description-") +#' dir.create(desc_dir) +#' writeLines( +#' '{"Name":"Example Dataset","BIDSVersion":"1.9.0","DatasetType":"raw"}', +#' file.path(desc_dir, "dataset_description.json") +#' ) +#' desc <- read_dataset_description(desc_dir) +#' dataset_name(desc) +#' dataset_type(desc) +#' unlink(desc_dir, recursive = TRUE) dataset_name <- function(x, ...) UseMethod("dataset_name") #' @rdname dataset_name #' @export diff --git a/R/mock_bids.R b/R/mock_bids.R index f54ba38c..7db9d1a7 100644 --- a/R/mock_bids.R +++ b/R/mock_bids.R @@ -522,7 +522,7 @@ create_mock_bids <- function(project_name, generated_event_paths <- character() # Keep track of event files defined generated_confound_paths <- character() # Keep track of confound files defined - for (i in 1:nrow(file_structure)) { + for (i in seq_len(nrow(file_structure))) { row <- file_structure[i, ] # Extract all potential BIDS entities from the row @@ -689,9 +689,11 @@ create_mock_bids <- function(project_name, if (row$fmriprep) { prep_dir_parts <- strsplit(prep_dir,"/")[[1]] # Check if path_parts start with prep_dir_parts and remove them - if (length(path_parts) >= length(prep_dir_parts) && - all(path_parts[1:length(prep_dir_parts)] == prep_dir_parts)) { - path_parts <- path_parts[-(1:length(prep_dir_parts))] + prep_dir_index <- seq_along(prep_dir_parts) + if (length(prep_dir_parts) > 0L && + length(path_parts) >= length(prep_dir_parts) && + all(path_parts[prep_dir_index] == prep_dir_parts)) { + path_parts <- path_parts[-prep_dir_index] } } diff --git a/R/pack_bids.R b/R/pack_bids.R index 7e7a46d1..5b99e7fc 100644 --- a/R/pack_bids.R +++ b/R/pack_bids.R @@ -915,7 +915,7 @@ list_pack_bids <- function(archive_path, verbose = TRUE) { # We need to extract to temp dir to get file sizes temp_extract <- tempfile("bids_list_") dir.create(temp_extract) - on.exit(unlink(temp_extract, recursive = TRUE)) + on.exit(unlink(temp_extract, recursive = TRUE), add = TRUE) untar(archive_path, exdir = temp_extract) diff --git a/inst/.DS_Store b/inst/.DS_Store deleted file mode 100644 index 9029965e..00000000 Binary files a/inst/.DS_Store and /dev/null differ diff --git a/inst/extdata/.DS_Store b/inst/extdata/.DS_Store deleted file mode 100644 index 45c5826a..00000000 Binary files a/inst/extdata/.DS_Store and /dev/null differ diff --git a/man/dataset_name.Rd b/man/dataset_name.Rd index 8253abf4..2da177e5 100644 --- a/man/dataset_name.Rd +++ b/man/dataset_name.Rd @@ -20,3 +20,15 @@ A character scalar. \description{ Get fields from a BIDS dataset description } +\examples{ +desc_dir <- tempfile("bids-description-") +dir.create(desc_dir) +writeLines( + '{"Name":"Example Dataset","BIDSVersion":"1.9.0","DatasetType":"raw"}', + file.path(desc_dir, "dataset_description.json") +) +desc <- read_dataset_description(desc_dir) +dataset_name(desc) +dataset_type(desc) +unlink(desc_dir, recursive = TRUE) +} diff --git a/tests/testthat/test_mock_bids.R b/tests/testthat/test_mock_bids.R index 65f7f4cc..dab95558 100644 --- a/tests/testthat/test_mock_bids.R +++ b/tests/testthat/test_mock_bids.R @@ -88,6 +88,28 @@ test_that("Mock BIDS project can be created", { expect_equal(mock_proj$prep_dir, "derivatives/mockprep") }) +test_that("create_mock_bids supports an empty file structure", { + empty_structure <- tibble::tibble( + subid = character(), + datatype = character(), + suffix = character(), + fmriprep = logical() + ) + + mock_proj <- create_mock_bids( + project_name = "EmptyMock", + participants = "01", + file_structure = empty_structure + ) + + expect_s3_class(mock_proj, "mock_bids_project") + expect_equal(mock_proj$name, "EmptyMock") + expect_equal(nrow(mock_proj$tbl), 0L) + expect_identical(mock_proj$subjects, character()) + expect_false(mock_proj$has_sessions) + expect_false(mock_proj$has_fmriprep) +}) + test_that("create_mock_bids accepts shorthand suffixes for stub datasets", { td <- tempfile("mock-bids-shorthand-") dir.create(td, recursive = TRUE) diff --git a/tests/testthat/test_vignette_resilience.R b/tests/testthat/test_vignette_resilience.R new file mode 100644 index 00000000..f4d39d72 --- /dev/null +++ b/tests/testthat/test_vignette_resilience.R @@ -0,0 +1,31 @@ +test_that("quickstart exits cleanly when the example dataset is unavailable", { + skip_if_not_installed("knitr") + + vignette_path <- testthat::test_path( + "..", "..", "vignettes", "quickstart.Rmd" + ) + if (!file.exists(vignette_path)) { + vignette_path <- system.file( + "doc", "quickstart.Rmd", package = "bidser" + ) + } + skip_if(!nzchar(vignette_path) || !file.exists(vignette_path)) + + knit_env <- new.env(parent = globalenv()) + knit_env$params <- list(family = "red", preset = "homage") + knit_env$get_example_bids_dataset <- function(...) { + stop("simulated offline failure") + } + output_path <- tempfile(fileext = ".md") + + expect_no_error( + knitr::knit( + vignette_path, + output = output_path, + envir = knit_env, + quiet = TRUE + ) + ) + output <- paste(readLines(output_path, warn = FALSE), collapse = "\n") + expect_match(output, "Example dataset not available.", fixed = TRUE) +}) diff --git a/vignettes/quickstart.Rmd b/vignettes/quickstart.Rmd index d836e077..93f6685e 100644 --- a/vignettes/quickstart.Rmd +++ b/vignettes/quickstart.Rmd @@ -95,8 +95,9 @@ ds001_path <- tryCatch( ) if (is.null(ds001_path)) { knitr::knit_exit("Example dataset not available.") +} else { + proj <- bids_project(ds001_path) } -proj <- bids_project(ds001_path) ``` ```{r}