diff --git a/R/build.R b/R/build.R index b09d14b..f03dfa5 100644 --- a/R/build.R +++ b/R/build.R @@ -41,7 +41,12 @@ #' VS.VSTESTCD == 'Heart Rate'" contains both #' VS.VSTESTCD and VS.VSSTRESN as prerequisites, and #' these columns will be kept through to the ADaM. -#' +#' @param verbose Character string controlling message verbosity. One of: +#' \describe{ +#' \item{`"message"`}{Show both warnings and messages (default)} +#' \item{`"warn"`}{Show warnings but suppress messages} +#' \item{`"silent"`}{Suppress all warnings and messages} +#' } #' #' @return dataset #' @export @@ -55,7 +60,8 @@ #' ds_list <- list(DM = read_xpt(metatools_example("dm.xpt"))) #' build_from_derived(spec, ds_list, predecessor_only = FALSE) build_from_derived <- function(metacore, ds_list, dataset_name = deprecated(), - predecessor_only = TRUE, keep = FALSE) { + predecessor_only = TRUE, keep = FALSE, + verbose = c("message", "warn", "silent")) { if (is_present(dataset_name)) { lifecycle::deprecate_warn( when = "0.2.0", @@ -68,6 +74,8 @@ build_from_derived <- function(metacore, ds_list, dataset_name = deprecated(), } verify_DatasetMeta(metacore) + verbose <- validate_verbose(verbose) + # Deprecate KEEP = TRUE keep <- match.arg(as.character(keep), c("TRUE", "FALSE", "ALL", "PREREQUISITE")) if (keep == "TRUE") { @@ -114,7 +122,7 @@ build_from_derived <- function(metacore, ds_list, dataset_name = deprecated(), str_to_lower() if (!all(ds_names %in% names(ds_list))) { unknown <- keep(names(ds_list), ~ !. %in% ds_names) - if (length(unknown) > 0) { + if (length(unknown) > 0 && check_warn(verbose)) { warning(paste0("The following dataset(s) have no predecessors and will be ignored:\n"), paste0(unknown, collapse = ", "), call. = FALSE @@ -124,11 +132,13 @@ build_from_derived <- function(metacore, ds_list, dataset_name = deprecated(), str_to_upper() %>% paste0(collapse = ", ") - message(paste0( - "Not all datasets provided. Only variables from ", - ds_using, - " will be gathered." - )) + if (check_message(verbose)) { + message(paste0( + "Not all datasets provided. Only variables from ", + ds_using, + " will be gathered." + )) + } # Filter out any variable that come from datasets that aren't present vars_w_ds <- vars_w_ds %>% @@ -162,7 +172,7 @@ build_from_derived <- function(metacore, ds_list, dataset_name = deprecated(), group_by(ds) %>% group_split() %>% map(get_variables, ds_list, keep, derirvations) %>% - prepare_join(join_by, names(ds_list)) %>% + prepare_join(join_by, names(ds_list), verbose) %>% reduce(full_join, by = join_by) } @@ -232,10 +242,12 @@ get_variables <- function(x, ds_list, keep, derivations) { #' #' @param x List of datasets with all columns added #' @param keys List of key values to join on +#' @param ds_names Names of datasets +#' @param verbose Verbosity level #' #' @return datasets #' @noRd -prepare_join <- function(x, keys, ds_names) { +prepare_join <- function(x, keys, ds_names, verbose = "message") { out <- list(x[[1]]) if (length(x) > 1) { @@ -248,8 +260,8 @@ prepare_join <- function(x, keys, ds_names) { intersect(colnames(x[[i]])) drop_cols <- c(drop_cols, conflicting_cols) - if (length(conflicting_cols) > 0) { - cli_inform(c("i" = "Dropping column(s) from {ds_names[[i]]} due to \\ + if (length(conflicting_cols) > 0 && check_message(verbose)) { + cli_inform(c("i" = "Dropping column(s) from {ds_names[[i]]} due to \ conflict with {ds_names[[j]]}: {conflicting_cols}.")) } } @@ -273,6 +285,12 @@ prepare_join <- function(x, keys, ds_names) { #' Note: Deprecated in version 0.2.0. The `dataset_name` argument will be removed #' in a future release. Please use `metacore::select_dataset` to subset the #' `metacore` object to obtain metadata for a single dataset. +#' @param verbose Character string controlling message verbosity. One of: +#' \describe{ +#' \item{`"message"`}{Show both warnings and messages (default)} +#' \item{`"warn"`}{Show warnings but suppress messages} +#' \item{`"silent"`}{Suppress all warnings and messages} +#' } #' #' @return Dataset with only specified columns #' @export @@ -287,7 +305,8 @@ prepare_join <- function(x, keys, ds_names) { #' select(USUBJID, SITEID) %>% #' mutate(foo = "Hello") #' drop_unspec_vars(data, spec) -drop_unspec_vars <- function(dataset, metacore, dataset_name = deprecated()) { +drop_unspec_vars <- function(dataset, metacore, dataset_name = deprecated(), + verbose = c("message", "warn", "silent")) { if (is_present(dataset_name)) { lifecycle::deprecate_warn( when = "0.2.0", @@ -299,6 +318,8 @@ drop_unspec_vars <- function(dataset, metacore, dataset_name = deprecated()) { metacore <- make_lone_dataset(metacore, dataset_name) } + verbose <- validate_verbose(verbose) + verify_DatasetMeta(metacore) var_list <- metacore$ds_vars %>% filter(is.na(supp_flag) | !(supp_flag)) %>% @@ -308,10 +329,12 @@ drop_unspec_vars <- function(dataset, metacore, dataset_name = deprecated()) { if (length(to_drop) > 0) { out <- dataset %>% select(-all_of(to_drop)) - message(paste0( - "The following variable(s) were dropped:\n ", - paste0(to_drop, collapse = "\n ") - )) + if (check_message(verbose)) { + message(paste0( + "The following variable(s) were dropped:\n ", + paste0(to_drop, collapse = "\n ") + )) + } } else { out <- dataset } diff --git a/R/labels.R b/R/labels.R index ac71488..4e9b778 100644 --- a/R/labels.R +++ b/R/labels.R @@ -75,6 +75,12 @@ remove_labels <- function(data) { #' Note: Deprecated in version 0.2.0. The `dataset_name` argument will be removed #' in a future release. Please use `metacore::select_dataset` to subset the #' `metacore` object to obtain metadata for a single dataset. +#' @param verbose Character string controlling message verbosity. One of: +#' \describe{ +#' \item{`"message"`}{Show both warnings and messages (default)} +#' \item{`"warn"`}{Show warnings but suppress messages} +#' \item{`"silent"`}{Suppress all warnings and messages} +#' } #' #' @return Dataframe with labels applied #' @export @@ -87,7 +93,8 @@ remove_labels <- function(data) { #' ) #' dm <- haven::read_xpt(metatools_example("dm.xpt")) #' set_variable_labels(dm, mc, dataset_name = "DM") -set_variable_labels <- function(data, metacore, dataset_name = deprecated()) { +set_variable_labels <- function(data, metacore, dataset_name = deprecated(), + verbose = c("message", "warn", "silent")) { if (is_present(dataset_name)) { lifecycle::deprecate_warn( when = "0.2.0", @@ -100,6 +107,8 @@ set_variable_labels <- function(data, metacore, dataset_name = deprecated()) { } verify_DatasetMeta(metacore) + verbose <- validate_verbose(verbose) + # Grab out the var names and labels var_spec <- metacore$var_spec %>% select(variable, label) @@ -113,12 +122,12 @@ set_variable_labels <- function(data, metacore, dataset_name = deprecated()) { in_meta <- setdiff(ns, dns) # Variables in metadata not in data in_data <- setdiff(dns, ns) # Variables in data not in metadata - if (length(in_meta) > 0) { + if (length(in_meta) > 0 && check_warn(verbose)) { wrn <- paste0("Variables in metadata not in data:\n\t", paste0(in_meta, collapse = "\n\t")) warning(wrn, call. = FALSE) } - if (length(in_data) > 0) { + if (length(in_data) > 0 && check_warn(verbose)) { wrn <- paste0("Variables in data not in metadata:\n\t", paste0(in_data, collapse = "\n\t")) warning(wrn, call. = FALSE) } diff --git a/R/supp.R b/R/supp.R index 6d40c90..98017a3 100644 --- a/R/supp.R +++ b/R/supp.R @@ -6,12 +6,21 @@ #' @param idvar IDVAR variable name (provided as a string) #' @param qeval QEVAL value to be populated for this QNAM #' @param qorig QORIG value to be populated for this QNAM +#' @param verbose Character string controlling message verbosity. One of: +#' \describe{ +#' \item{`"message"`}{Show both warnings and messages (default)} +#' \item{`"warn"`}{Show warnings but suppress messages} +#' \item{`"silent"`}{Suppress all warnings and messages} +#' } #' #' @return Observations structured in SUPP format #' @export #' #' -build_qnam <- function(dataset, qnam, qlabel, idvar, qeval, qorig) { +build_qnam <- function(dataset, qnam, qlabel, idvar, qeval, qorig, + verbose = c("message", "warn", "silent")) { + verbose <- validate_verbose(verbose) + # Need QNAM as a variable qval <- as.symbol(qnam) @@ -58,7 +67,9 @@ build_qnam <- function(dataset, qnam, qlabel, idvar, qeval, qorig) { blank_test <- out %>% pull(QVAL) if (any(blank_test == "")) { - message(paste0("Empty QVAL rows removed for QNAM = ", unique(out$QNAM))) + if (check_message(verbose)) { + message(paste0("Empty QVAL rows removed for QNAM = ", unique(out$QNAM))) + } out <- out %>% filter(QVAL != "") } diff --git a/R/utils.R b/R/utils.R index f78c30c..216ec89 100644 --- a/R/utils.R +++ b/R/utils.R @@ -44,3 +44,32 @@ make_lone_dataset <- function(metacore, dataset_name) { } metacore } + +#' Check if messages should be displayed +#' @param verbose Verbosity level +#' @noRd +check_message <- function(verbose) { + verbose == "message" +} + +#' Check if warnings should be displayed +#' @param verbose Verbosity level +#' @noRd +check_warn <- function(verbose) { + verbose %in% c("message", "warn") +} + +#' Validate verbose parameter +#' @param verbose Verbosity level to validate +#' @noRd +validate_verbose <- function(verbose, arg = rlang::caller_arg(verbose), call = rlang::caller_env()) { + choices <- c("message", "warn", "silent") + tryCatch( + match.arg(verbose, choices), + error = function(e) { + cli_abort(c( + "x" = "{.arg {arg}} should be one of: {cli::ansi_collapse(choices, last = ', ')}" + ), call = call) + } + ) +} diff --git a/man/build_from_derived.Rd b/man/build_from_derived.Rd index 404840d..d75e421 100644 --- a/man/build_from_derived.Rd +++ b/man/build_from_derived.Rd @@ -9,7 +9,8 @@ build_from_derived( ds_list, dataset_name = deprecated(), predecessor_only = TRUE, - keep = FALSE + keep = FALSE, + verbose = c("message", "warn", "silent") ) } \arguments{ @@ -54,6 +55,13 @@ VS.VSTESTCD == 'Heart Rate'" contains both VS.VSTESTCD and VS.VSSTRESN as prerequisites, and these columns will be kept through to the ADaM. }} + +\item{verbose}{Character string controlling message verbosity. One of: +\describe{ +\item{\code{"message"}}{Show both warnings and messages (default)} +\item{\code{"warn"}}{Show warnings but suppress messages} +\item{\code{"silent"}}{Suppress all warnings and messages} +}} } \value{ dataset diff --git a/man/build_qnam.Rd b/man/build_qnam.Rd index 80afb31..e820ec6 100644 --- a/man/build_qnam.Rd +++ b/man/build_qnam.Rd @@ -4,7 +4,15 @@ \alias{build_qnam} \title{Build the observations for a single QNAM} \usage{ -build_qnam(dataset, qnam, qlabel, idvar, qeval, qorig) +build_qnam( + dataset, + qnam, + qlabel, + idvar, + qeval, + qorig, + verbose = c("message", "warn", "silent") +) } \arguments{ \item{dataset}{Input dataset} @@ -18,6 +26,13 @@ build_qnam(dataset, qnam, qlabel, idvar, qeval, qorig) \item{qeval}{QEVAL value to be populated for this QNAM} \item{qorig}{QORIG value to be populated for this QNAM} + +\item{verbose}{Character string controlling message verbosity. One of: +\describe{ +\item{\code{"message"}}{Show both warnings and messages (default)} +\item{\code{"warn"}}{Show warnings but suppress messages} +\item{\code{"silent"}}{Suppress all warnings and messages} +}} } \value{ Observations structured in SUPP format diff --git a/man/drop_unspec_vars.Rd b/man/drop_unspec_vars.Rd index 5eb0c1d..dff1653 100644 --- a/man/drop_unspec_vars.Rd +++ b/man/drop_unspec_vars.Rd @@ -4,7 +4,12 @@ \alias{drop_unspec_vars} \title{Drop Unspecified Variables} \usage{ -drop_unspec_vars(dataset, metacore, dataset_name = deprecated()) +drop_unspec_vars( + dataset, + metacore, + dataset_name = deprecated(), + verbose = c("message", "warn", "silent") +) } \arguments{ \item{dataset}{Dataset to change} @@ -18,6 +23,13 @@ been subsetted.\cr Note: Deprecated in version 0.2.0. The \code{dataset_name} argument will be removed in a future release. Please use \code{metacore::select_dataset} to subset the \code{metacore} object to obtain metadata for a single dataset.} + +\item{verbose}{Character string controlling message verbosity. One of: +\describe{ +\item{\code{"message"}}{Show both warnings and messages (default)} +\item{\code{"warn"}}{Show warnings but suppress messages} +\item{\code{"silent"}}{Suppress all warnings and messages} +}} } \value{ Dataset with only specified columns diff --git a/man/set_variable_labels.Rd b/man/set_variable_labels.Rd index 282156a..833bd7e 100644 --- a/man/set_variable_labels.Rd +++ b/man/set_variable_labels.Rd @@ -4,7 +4,12 @@ \alias{set_variable_labels} \title{Apply labels to a data frame using a metacore object} \usage{ -set_variable_labels(data, metacore, dataset_name = deprecated()) +set_variable_labels( + data, + metacore, + dataset_name = deprecated(), + verbose = c("message", "warn", "silent") +) } \arguments{ \item{data}{A dataframe or tibble upon which labels will be applied} @@ -18,6 +23,13 @@ object provided hasn't already been subsetted.\cr Note: Deprecated in version 0.2.0. The \code{dataset_name} argument will be removed in a future release. Please use \code{metacore::select_dataset} to subset the \code{metacore} object to obtain metadata for a single dataset.} + +\item{verbose}{Character string controlling message verbosity. One of: +\describe{ +\item{\code{"message"}}{Show both warnings and messages (default)} +\item{\code{"warn"}}{Show warnings but suppress messages} +\item{\code{"silent"}}{Suppress all warnings and messages} +}} } \value{ Dataframe with labels applied diff --git a/tests/testthat/test-build.R b/tests/testthat/test-build.R index d0903ca..7bd365f 100644 --- a/tests/testthat/test-build.R +++ b/tests/testthat/test-build.R @@ -175,7 +175,7 @@ test_that("build_from_derived", { }) -test_that("add_variables", { +test_that("add_variables add missing variables to the metacore object", { load(metacore::metacore_example("pilot_ADaM.rda")) spec <- metacore %>% select_dataset("ADSL", quiet = TRUE) data <- haven::read_xpt(metatools_example("adsl.xpt")) %>% @@ -199,3 +199,75 @@ test_that("add_variables", { data ) }) + +test_that("drop_unspec_vars verbose parameter", { + data <- haven::read_xpt(metatools_example("adsl.xpt")) %>% + mutate(foo = "Hello", foo2 = "world") + + man_vars <- metacore$ds_vars %>% + filter(dataset == "ADSL") %>% + pull(variable) + man_dat <- data %>% + select(all_of(man_vars)) + + # Test verbose = "message" (default behavior) + expect_message( + drop_unspec_vars(data, spec, verbose = "message"), + "The following variable\\(s\\) were dropped:" + ) + + # Test verbose = "warn" (suppress messages) + expect_silent( + drop_unspec_vars(data, spec, verbose = "warn") + ) + + # Test verbose = "silent" (suppress all output) + expect_silent( + drop_unspec_vars(data, spec, verbose = "silent") + ) + + # Verify all verbose levels return same result + result_message <- drop_unspec_vars(data, spec, verbose = "message") + result_warn <- drop_unspec_vars(data, spec, verbose = "warn") + result_silent <- drop_unspec_vars(data, spec, verbose = "silent") + + expect_equal(result_message, man_dat) + expect_equal(result_warn, man_dat) + expect_equal(result_silent, man_dat) + + # Test invalid verbose value + expect_error( + drop_unspec_vars(data, spec, verbose = "invalid"), + "should be one of: message, warn, silent" + ) +}) + +test_that("build_from_derived verbose controls prepare_join messages", { + load(metacore::metacore_example("pilot_ADaM.rda")) + spec2 <- metacore %>% select_dataset("ADAE", quiet = TRUE) + + # Use safetyData datasets and add a conflicting non-key column + # STUDYID is a key column, so add a different column that will conflict + ae <- safetyData::sdtm_ae %>% + mutate(TESTCOL = "AE_VALUE") # Add a non-key column + + adsl <- safetyData::adam_adsl %>% + mutate(TESTCOL = "ADSL_VALUE") # Same column with different value + + ds_list <- list(AE = ae, ADSL = adsl) + + # Test that conflicting column messages are shown with verbose = "message" + expect_message( + build_from_derived(spec2, ds_list, predecessor_only = FALSE, verbose = "message", keep = "ALL"), + "Dropping column\\(s\\) from" + ) + + # Test that messages are suppressed with verbose = "warn" and "silent" + expect_silent( + build_from_derived(spec2, ds_list, predecessor_only = FALSE, verbose = "warn", keep = "ALL") + ) + + expect_silent( + build_from_derived(spec2, ds_list, predecessor_only = FALSE, verbose = "silent", keep = "ALL") + ) +}) diff --git a/tests/testthat/test-labels.R b/tests/testthat/test-labels.R index 4372853..dbe9ea0 100644 --- a/tests/testthat/test-labels.R +++ b/tests/testthat/test-labels.R @@ -58,7 +58,8 @@ mc <- suppressWarnings( ) ) -test_that("Check that add_labels applies labels properly", { +# add_labels() tests ---- +test_that("add_labels applies labels properly", { x <- mtcars %>% add_labels( mpg = "Miles Per Gallon", @@ -69,14 +70,15 @@ test_that("Check that add_labels applies labels properly", { expect_equal(attr(x$cyl, "label"), "Cylinders") }) -test_that("Check that add_labels errors properly", { +test_that("add_labels errors on invalid input", { expect_error(add_labels(TRUE, x = "label")) expect_error(add_labels(mtcars, "label")) expect_error(add_labels(mtcars, bad = "label")) expect_error(add_labels(mtcars, mpg = 1)) }) -test_that("set_variable_labels applies labels properly", { +# set_variable_labels() tests ---- +test_that("set_variable_labels applies labels from metacore properly", { # Load in the metacore test object and example data suppressMessages( mc <- metacore::spec_to_metacore(metacore::metacore_example("p21_mock.xlsx"), quiet = TRUE) %>% @@ -94,7 +96,7 @@ test_that("set_variable_labels applies labels properly", { expect_equal(labs, mc$var_spec$label) }) -test_that("set_variable_labels raises warnings properly", { +test_that("set_variable_labels warns on variable mismatches", { # This is metadata for the dplyr::starwars dataset mc <- suppressWarnings( suppressMessages( @@ -102,97 +104,79 @@ test_that("set_variable_labels raises warnings properly", { ) ) %>% select_dataset("Starwars", quiet = TRUE) + # Variables in data not in metadata starwars_short2 <- starwars_short starwars_short2$new_var <- "" - - # Variables in data not in metadata expect_warning(set_variable_labels(starwars_short2, mc)) - mc <- suppressWarnings( + # Variables in metadata not in data + mc_subset <- suppressWarnings( suppressMessages( metacore::metacore(ds_spec, ds_vars[1:4, ], var_spec[1:4, ], value_spec, derivations, code_id) %>% metacore::select_dataset("Starwars", quiet = TRUE) ) ) - expect_warning(set_variable_labels(starwars_short, mc)) -}) - -test_that("remove_labels works to remove all labels", { - # Create test data as tibble to match what remove_labels returns - data <- tibble::as_tibble(mtcars[1:2, 1:2], rownames = NULL) - - data_lab <- data %>% - purrr::map2_dfc(c("apple", "pear"), function(x, y) { - attr(x, "label") <- y - x - }) - - remove_labels(data_lab) %>% - expect_equal(., data) - - expect_error(remove_labels(c(1:10))) + expect_warning(set_variable_labels(starwars_short, mc_subset)) }) -test_that("set_variable_labels correctly identifies variable mismatches", { +test_that("set_variable_labels respects verbose parameter", { load(metacore::metacore_example("pilot_SDTM.rda")) - spec <- metacore %>% select_dataset("DM", quiet = TRUE) - + spec <- metacore |> select_dataset("DM", quiet = TRUE) dm <- haven::read_xpt(metatools_example("dm.xpt")) - # Get the actual variables in the metadata - meta_vars <- spec$var_spec$variable - data_vars <- names(dm) - - # Find a variable that exists in both to manipulate - common_var <- intersect(meta_vars, data_vars)[1] - - # Test 1: Variable in metadata but NOT in data (should trigger first warning) - dm_missing_var <- dm %>% - select(-all_of(common_var)) + # Create data with mismatch to trigger warnings + dm_mismatch <- dm |> + select(-RACE) |> + mutate(EXTRAVAR = "test") - expect_warning( - set_variable_labels(dm_missing_var, spec), - "Variables in metadata not in data" + # verbose = "silent" suppresses warnings + expect_silent( + set_variable_labels(dm_mismatch, spec, verbose = "silent") ) - # Verify the specific variable is mentioned in the warning + # verbose = "message" shows warnings expect_warning( - set_variable_labels(dm_missing_var, spec), - common_var + set_variable_labels(dm_mismatch, spec, verbose = "message"), + "Variables in" ) - # Test 2: Variable in data but NOT in metadata (should trigger second warning) - dm_extra_var <- dm %>% - mutate(EXTRAVAR = "test") - + # verbose = "warn" shows warnings expect_warning( - set_variable_labels(dm_extra_var, spec), - "Variables in data not in metadata" + set_variable_labels(dm_mismatch, spec, verbose = "warn"), + "Variables in" ) - expect_warning( - set_variable_labels(dm_extra_var, spec), - "EXTRAVAR" + # Invalid verbose value errors + expect_error( + set_variable_labels(dm, spec, verbose = "invalid"), + "should be one of" ) +}) - # Test 3: Both types of mismatches (should trigger both warnings) - dm_both_mismatch <- dm %>% - select(-all_of(common_var)) %>% - mutate(EXTRAVAR = "test") +# remove_labels() tests ---- +test_that("remove_labels removes labels properly", { + # Add labels first + x <- mtcars |> + add_labels( + mpg = "Miles Per Gallon", + cyl = "Cylinders" + ) - result <- suppressWarnings( - set_variable_labels(dm_both_mismatch, spec) - ) + # Verify labels exist + expect_equal(attr(x$mpg, "label"), "Miles Per Gallon") + expect_equal(attr(x$cyl, "label"), "Cylinders") - # Should get exactly 2 warnings - expect_warning( - set_variable_labels(dm_both_mismatch, spec), - "Variables in" - ) + # Remove labels + x_no_labels <- remove_labels(x) + + # Verify labels are gone + expect_null(attr(x_no_labels$mpg, "label")) + expect_null(attr(x_no_labels$cyl, "label")) +}) - # Verify labels still applied to matching variables - matching_vars <- intersect(names(dm_both_mismatch), meta_vars) - if (length(matching_vars) > 0) { - expect_true(!is.null(attr(result[[matching_vars[1]]], "label"))) - } +test_that("remove_labels errors on invalid input", { + expect_error( + remove_labels("not a dataframe"), + "Labels must be removed from a data.frame or tibble" + ) }) diff --git a/tests/testthat/test-supp.R b/tests/testthat/test-supp.R index 235a2ec..3a6894c 100644 --- a/tests/testthat/test-supp.R +++ b/tests/testthat/test-supp.R @@ -295,3 +295,83 @@ test_that("combine_supp() does not create an IDVARVAL column (#78)", { noidvarval <- combine_supp(simple_ae, simple_suppae) expect_false("IDVARVAL" %in% names(noidvarval)) }) + +test_that("build_qnam verbose parameter", { + # Create simple test data with a column that will be used as QNAM + ae <- safetyData::sdtm_ae %>% + head(10) %>% + mutate(TESTVAR = c("", "", "Y", "Y", "N", "", "Y", "N", "Y", "")) # Some empty strings + + # Test verbose = "message" (default) - should show message about empty QVAL + expect_message( + build_qnam( + dataset = ae, + qnam = "TESTVAR", + qlabel = "Test Variable", + idvar = "AESEQ", + qeval = "INVESTIGATOR", + qorig = "CRF", + verbose = "message" + ), + "Empty QVAL rows removed for QNAM = TESTVAR" + ) + + # Test verbose = "warn" - suppress messages + expect_silent( + result_warn <- build_qnam( + dataset = ae, + qnam = "TESTVAR", + qlabel = "Test Variable", + idvar = "AESEQ", + qeval = "INVESTIGATOR", + qorig = "CRF", + verbose = "warn" + ) + ) + + # Test verbose = "silent" - suppress all output + expect_silent( + result_silent <- build_qnam( + dataset = ae, + qnam = "TESTVAR", + qlabel = "Test Variable", + idvar = "AESEQ", + qeval = "INVESTIGATOR", + qorig = "CRF", + verbose = "silent" + ) + ) + + # Verify all verbose levels return same result + result_message <- suppressMessages( + build_qnam( + dataset = ae, + qnam = "TESTVAR", + qlabel = "Test Variable", + idvar = "AESEQ", + qeval = "INVESTIGATOR", + qorig = "CRF", + verbose = "message" + ) + ) + + expect_equal(result_message, result_warn) + expect_equal(result_message, result_silent) + + # Verify empty strings were actually removed + expect_false("" %in% result_message$QVAL) + + # Test invalid verbose value + expect_error( + build_qnam( + dataset = ae, + qnam = "TESTVAR", + qlabel = "Test Variable", + idvar = "AESEQ", + qeval = "INVESTIGATOR", + qorig = "CRF", + verbose = "invalid" + ), + "should be one of: message, warn, silent" + ) +})