tests/testthat/test-tidyselect_integration.R

tbl <- data.frame(x = 1:2, y = 1:2, nonunique = "A")
exist_col <- "y"
nonunique_col <- "nonunique"
nonexist_col <- "z"
z <- rlang::missing_arg()

test_that("Backwards compatibility with `vars()`", {
  
  # Bare symbol selects column(s)
  expect_success(expect_rows_distinct(tbl, vars(x)))
  expect_success(expect_rows_distinct(tbl, vars(x, nonunique)))
  expect_failure(expect_rows_distinct(tbl, vars(nonunique)))
  
  # Bare character selects column(s)
  expect_success(expect_rows_distinct(tbl, vars("x")))
  expect_success(expect_rows_distinct(tbl, vars("x", "nonunique")))
  expect_failure(expect_rows_distinct(tbl, vars("nonunique")))
  
  # Bang-bang in-lines value
  expect_success(expect_rows_distinct(tbl, vars(!!exist_col)))
  expect_failure(expect_rows_distinct(tbl, vars(!!nonunique_col)))
  
  # `vars()` wrapping tidyselect expressions is redundant but continues to work
  expect_success(expect_rows_distinct(tbl, vars(all_of("x"))))
  
  # `vars()` selection of 0-columns errors *only* in non-validation-planning contexts
  expect_error(rows_distinct(tbl, vars("z")))
  expect_error(expect_rows_distinct(tbl, vars("z")))
  expect_error(test_rows_distinct(tbl, vars("z")))
  expect_no_error(tbl %>% create_agent() %>% rows_distinct(vars("z")))
  expect_no_error(tbl %>% create_agent() %>% rows_distinct(vars("z")) %>% interrogate())
  
})

test_that("Full range of tidyselect features available in column selection", {
  
  # Single symbol
  expect_success(expect_rows_distinct(tbl, x))
  expect_failure(expect_rows_distinct(tbl, nonunique))
  
  # Preferred {tidyselect}-style `c()` syntax
  expect_success(expect_rows_distinct(tbl, c(x)))
  expect_success(expect_rows_distinct(tbl, c(x, nonunique)))
  expect_failure(expect_rows_distinct(tbl, c(nonunique)))
  
  # {tidyselect} functions
  expect_success(expect_rows_distinct(tbl, tidyselect::all_of("x")))
  expect_success(expect_rows_distinct(tbl, tidyselect::all_of(c("x", "nonunique"))))
  expect_failure(expect_rows_distinct(tbl, tidyselect::all_of("nonunique")))
  
  # NEW: {tidyselect} integer indexing
  expect_success(expect_rows_distinct(tbl, 1))
  expect_success(expect_rows_distinct(tbl, c(1, 3)))
  expect_failure(expect_rows_distinct(tbl, 3))
  
  # NEW: {tidyselect} negative indexing
  expect_success(expect_rows_distinct(tbl, -(2:3)))
  expect_success(expect_rows_distinct(tbl, -2))
  expect_failure(expect_rows_distinct(tbl, -(1:2)))
  
  # NEW: {tidyselect} `where()` predicate:
  expect_success(expect_rows_distinct(tbl, !tidyselect::where(is.character)))
  expect_success(expect_rows_distinct(tbl, tidyselect::where(is.numeric)))
  expect_error(expect_rows_distinct(tbl, tidyselect::where(is.character)))
  
  # NEW: {tidyselect} functions in complex expressions
  expect_success(expect_rows_distinct(tbl, c(x, tidyselect::all_of(exist_col))))
  expect_error(expect_rows_distinct(tbl, c(x, tidyselect::all_of(nonexist_col))))
  expect_success(expect_rows_distinct(tbl, c(x, tidyselect::any_of(nonexist_col))))
  
  # Supplying a character vector variable still works, but signals deprecation:
  rlang::local_options(lifecycle_verbosity = "warning")
  expect_success(expect_warning(
    expect_rows_distinct(tbl, exist_col),
    "Using an external vector in selections was deprecated in tidyselect 1.1.0."
  ))
  
})

test_that("'NULL = select everything' behavior in rows_*() validation functions", {

  # For `rows_*()` functions specifically, empty/NULL = "select everything" behavior:
  expect_success(expect_rows_distinct(data.frame(x = 1, y = 2)))
  expect_success(expect_rows_complete(data.frame(x = 1, y = 2)))
  expect_failure(expect_rows_distinct(data.frame(x = c(1, 1))))
  expect_failure(expect_rows_complete(data.frame(x = c(1, NA))))
  expect_success(expect_rows_distinct(data.frame(x = 1, y = 2), columns = NULL))
  expect_success(expect_rows_complete(data.frame(x = 1, y = 2), columns = NULL))
  expect_failure(expect_rows_distinct(data.frame(x = c(1, 1)), columns = NULL))
  expect_failure(expect_rows_complete(data.frame(x = c(1, NA)), columns = NULL))
  
  # Report shows all column names with empty `columns` argument
  expect_equal({
    small_table %>%
      create_agent() %>% 
      rows_distinct() %>% 
      rows_complete() %>% 
      interrogate() %>% 
      {.$validation_set$column} %>% 
      unlist() %>% 
      unique()
  }, toString(colnames(small_table)))
  
  # Report shows all column names with explicit NULL `columns` argument
  expect_equal({
    small_table %>%
      create_agent() %>% 
      rows_distinct(columns = NULL) %>% 
      rows_complete(columns = NULL) %>% 
      interrogate() %>% 
      {.$validation_set$column} %>% 
      unlist() %>% 
      unique()
  }, toString(colnames(small_table)))
    
})

test_that("tidyselect coverage for `col_exists()`", {
  
  # Reprex from (#433)
  df <- tibble::tibble(
    id.x = 1:3,
    id.y = 1:3,
    stuff = 1:3
  )
  expect_success({
    df %>% 
      expect_col_exists(
        columns = vars(ends_with(".x"))
      )
  })
  expect_equal({
    df %>% 
      col_exists(
        columns = vars(ends_with(".x"))
      )
  }, df)
  
  # Multiple column selection produces multiple steps
  expect_no_error({
    df_interrogated <- df %>% 
      create_agent() %>% 
      col_exists(starts_with("id")) %>% 
      interrogate()
  })
  expect_equal(nrow(df_interrogated$validation_set), 2L)
  
})

test_that("error/failure patterns for `col_exists`", {
  
  # Selecting non-existent columns signals failure
  expect_error(expect_failure({
    small_table %>% 
      col_exists("z")
  }))
  expect_failure({
    small_table %>% 
      expect_col_exists("z")
  })
  
  # 0-column tidyselect selection signals failure
  expect_error(expect_failure({
    small_table %>% 
      col_exists(starts_with("z"))
  }))
  expect_failure({
    small_table %>% 
      expect_col_exists("z")
  })
  
  # Unrelated evaluation errors should be chained and rethrown
  expect_error({
    small_table %>% 
      col_exists(stop("Error!"))
  }, "Error!")
  expect_error({
    small_table %>% 
      expect_col_exists(stop("Error!"))
  }, "Error!")
  expect_error({
    small_table %>% 
      test_col_exists(stop("Error!"))
  }, "Error!")
  
  # Test should return FALSE for 0-column and non-existent column
  expect_false({
    small_table %>% 
      test_col_exists("z")
  })
  expect_false({
    small_table %>% 
      test_col_exists("z")
  })
  
  # No failure/error during validation
  expect_no_error({
    agent_nonexist_col <- create_agent(small_table) %>% 
      col_exists("z") %>% 
      interrogate()
  })
  expect_false(all_passed(agent_nonexist_col))
  expect_no_error({
    agent_tidyselect_0col <- create_agent(small_table) %>% 
      col_exists(starts_with("z")) %>% 
      interrogate()
  })
  expect_false(all_passed(agent_tidyselect_0col))
  
})

test_that("c()-expr works for serially", {
  
  # Example from `serially()` docs
  tbl <-
    dplyr::tibble(
      a = c(5, 2, 6),
      b = c(6, 4, 9),
      c = c(1, 2, 3)
    )
  agent_1 <-
    create_agent(tbl = tbl) %>%
    serially(
      ~ test_col_is_numeric(., columns = vars(a, b)),
      ~ test_col_vals_not_null(., columns = vars(a, b)),
      ~ col_vals_gt(., columns = vars(b), value = vars(a))
    ) %>%
    interrogate()
  expect_no_error({
    agent_1_c <-
      create_agent(tbl = tbl) %>%
      serially(
        ~ test_col_is_numeric(., columns = c(a, b)),
        ~ test_col_vals_not_null(., columns = c(a, b)),
        ~ col_vals_gt(., columns = b, value = vars(a))
      ) %>%
      interrogate()
  })
  expect_identical(
    get_agent_report(agent_1, display_table = FALSE)$n_pass,
    get_agent_report(agent_1_c, display_table = FALSE)$n_pass
  )
  
})
rich-iannone/pointblank documentation built on June 29, 2024, 4:09 p.m.