test_that("select() is generic", {
  expect_identical(
    select(set_names(letters), b:c),
    c(b = "b", c = "c")
  )
  expect_identical(
    select(as.list(set_names(letters)), b:c),
    list(b = "b", c = "c")
  )
})

test_that("select() supports existing duplicates", {
  x <- list(a = 1, b = 2, a = 3)
  expect_identical(select(x, A = a), list(A = 1, A = 3))
})

test_that("absent `sel` leads to empty selection", {
  expect_identical(select_loc(mtcars), named(int()))
  expect_identical(select_loc(data.frame()), named(int()))
})

test_that("zero-length vector `sel` leads to empty selection (#214)", {
  expect_identical(select_loc(mtcars, character()), named(int()))
  expect_identical(select_loc(data.frame(), character()), named(int()))
})

test_that("can specify inclusion and exclusion", {
  x <- list(a = 1, b = 2, c = 3)
  expect_identical(select_loc(x, int(), include = "b"), c(b = 2L))
  expect_identical(select_loc(x, -int(), exclude = c("a", "c")), c(b = 2L))

  # Can exclude variables that don't exist
  expect_identical(select_loc(x, 2, exclude = "d"), c(b = 2L))
})

test_that("included variables added to front", {
  x <- list(a = 1, b = 2, c = 3)
  expect_named(select_loc(x, "a", include = "b"), c("b", "a"))
  expect_named(select_loc(x, "c", include = "b"), c("b", "c"))

  # but only if not already present
  expect_named(select_loc(x, c("a", "b"), include = "b"), c("a", "b"))
  expect_named(select_loc(x, c("b", "a"), include = "b"), c("b", "a"))

  state <- 0L
  fn <- fn <- function() {
    state <<- state + 1L
    "b"
  }

  select_loc(x, c("a", fn()), include = "b")
  expect_equal(state, 1)
})

test_that("include and exclude validate their inputs", {
  expect_snapshot({
    x <- list(a = 1, b = 2, c = 3)
    (expect_error(select_loc(x, "a", include = 1)))
    (expect_error(select_loc(x, "a", include = "d")))
    (expect_error(select_loc(x, "a", exclude = 1)))
  })
})

test_that("variables are excluded with non-strict `any_of()`", {
  expect_identical(
    select_loc(iris, 1:3, exclude = "foo"),
    select_loc(iris, 1:3)
  )
})

test_that("select_loc() checks inputs", {
  expect_error(select_loc(function() NULL), class = "vctrs_error_scalar_type")
})

test_that("select_loc() accepts name spec", {
  expect_identical(
    select_loc(mtcars, c(foo = c(mpg, cyl)), name_spec = "{outer}_{inner}"),
    c(foo_1 = 1L, foo_2 = 2L)
  )
})

test_that("result is named even with constant inputs (#173)", {
  expect_identical(
    eval_select("Sepal.Width", iris),
    c(Sepal.Width = 2L)
  )
})

test_that("can forbid rename syntax (#178)", {
  expect_snapshot({
    (expect_error(select_loc(mtcars, c(foo = cyl), allow_rename = FALSE)))
    (expect_error(select_loc(mtcars, c(cyl, foo = cyl), allow_rename = FALSE)))
    (expect_error(select_loc(mtcars, c(cyl, foo = mpg), allow_rename = FALSE)))
    (expect_error(select_loc(mtcars, c(foo = mpg, cyl), allow_rename = FALSE)))
  })

  expect_named(select_loc(mtcars, starts_with("c") | all_of("am"), allow_rename = FALSE), c("cyl", "carb", "am"))
})

test_that("can forbid empty selections", {
  expect_snapshot(error = TRUE, {
    select_loc(mtcars, allow_empty = FALSE)
    select_loc(mtcars, integer(), allow_empty = FALSE)
    select_loc(mtcars, starts_with("z"), allow_empty = FALSE)
  })
})

test_that("eval_select() errors mention correct calls", {
  f <- function() stop("foo")
  expect_snapshot((expect_error(select_loc(mtcars, f()))))
})

test_that("predicate outputs are type-checked", {
  expect_snapshot({
    (expect_error(select_loc(mtcars, function(x) "")))
  })
})

test_that("eval_select() produces correct backtraces", {
  f <- function(base) g(base)
  g <- function(base) h(base)
  h <- function(base) if (base) stop("foo") else abort("foo")

  local_options(
    rlang_trace_trop_env = current_env(),
    rlang_trace_format_srcrefs = FALSE
  )

  expect_snapshot({
    print(expect_error(select_loc(mtcars, f(base = TRUE))))
    print(expect_error(select_loc(mtcars, f(base = FALSE))))
  })
})

test_that("eval_select() produces correct chained errors", {
  expect_snapshot({
    (expect_error(select_loc(mtcars, 1 + "")))

    f <- function() 1 + ""
    (expect_error(select_loc(mtcars, f())))
  })
})

test_that("can select with predicate when `allow_rename` is `FALSE` (#225)", {
  sel <- eval_select(
    expr(where(is.numeric)),
    mtcars,
    allow_rename = FALSE
  )
  expect_equal(sel, set_names(seq_along(mtcars), names(mtcars)))
})

test_that("location must resolve to numeric variables throws error", {
  expect_error(
    select_loc(letters2, !!list()),
    class = "vctrs_error_subscript_type"
  )
  expect_error(
    select_loc(letters2, !!env()),
    class = "vctrs_error_subscript_type"
  )
})

test_that("order is determined from inputs (#53)", {
  expect_identical(
    select_loc(mtcars, c(starts_with("c"), starts_with("d"))),
    c(cyl = 2L, carb = 11L, disp = 3L, drat = 5L)
  )
  expect_identical(
    select_loc(mtcars, one_of(c("carb", "mpg"))),
    c(carb = 11L, mpg = 1L)
  )
})


test_that("middle (no-match) selector should not clear previous selectors (issue #2275)", {
  cn <- setNames(nm = c("x", "y", "z"))

  expect_equal(
    select_loc(cn, c(contains("x"), contains("foo"), contains("z"))),
    c(x = 1L, z = 3L)
  )
  expect_equal(
    select_loc(cn, c(contains("x"), -contains("foo"), contains("z"))),
    c(x = 1L, z = 3L)
  )
})

test_that("selecting with predicate + rename doesn't duplicate", {
  x <- list(a = "x", b = "y", c = 1, d = 2)
  expect_equal(select_loc(x, c(where(is.numeric), C = c)), c(C = 3, d = 4))
  expect_equal(select_loc(x, c(where(is.numeric), A = a)), c(c = 3, d = 4, A = 1))
})
