
test_that("basic assert is idempotent", {
  x <- new_vctr(1:4)

  expect_true(vec_is(x))
  expect_identical(vec_assert(x), x)

  expect_identical(vec_assert(x), new_vctr(1:4))
  expect_false(withVisible(vec_assert(x))$visible)

  expect_true(vec_is(1:4))
  expect_identical(vec_assert(1:4), 1:4)
})

test_that("asserting ptype", {
  x <- new_vctr(1:4)
  good <- new_vctr(integer())

  expect_true(vec_is(x, good))
  expect_error(vec_assert(x, good), NA)

  # Is this the correct error message?
  bad <- new_vctr(double())
  expect_false(vec_is(x, bad))
  expect_error(vec_assert(x, bad), class = "vctrs_error_assert_ptype")
})

test_that("asserting size", {
  x <- new_vctr(1:4)

  expect_true(vec_is(x, size = 4))
  expect_error(vec_assert(x, size = 4), NA)

  expect_false(vec_is(x, size = 5))
  expect_error(vec_assert(x, size = 5), class = "vctrs_error_assert_size")
})

test_that("vec_assert() labels input", {
  expect_error(
    vec_assert(new_vctr(1:4), size = 5),
    regexp = "`new_vctr\\(1:4\\)` must have",
    class = "vctrs_error_assert_size"
  )
  expect_error(
    vec_assert(new_vctr(1:4), size = 5, arg = "foobar"),
    regexp = "`foobar` must have",
    class = "vctrs_error_assert_size"
  )
})

test_that("bare atomic vectors are vectors but not recursive", {
  expect_true(obj_is_vector(TRUE))
  expect_true(obj_is_vector(1L))
  expect_true(obj_is_vector(1))
  expect_true(obj_is_vector(1i))
  expect_true(obj_is_vector("foo"))
  expect_true(obj_is_vector(as.raw(1)))
})

test_that("S3 atomic vectors are vectors", {
  expect_true(obj_is_vector(foobar(TRUE)))
  expect_true(obj_is_vector(foobar(1L)))
  expect_true(obj_is_vector(foobar(1)))
  expect_true(obj_is_vector(foobar(1i)))
  expect_true(obj_is_vector(foobar("foo")))
  expect_true(obj_is_vector(foobar(as.raw(1))))
})

test_that("bare lists are vectors", {
  expect_true(obj_is_vector(list()))
})

test_that("S3 lists are not vectors by default", {
  expect_false(obj_is_vector(foobar()))
  expect_false(obj_is_list(foobar()))

  local_foobar_proxy()

  # TODO: These seem inconsistent.
  # Should we require that S3 list proxies satisfy `obj_is_list()`?
  # (i.e. unclass themselves or explicitly inherit from `"list"`?)
  expect_true(obj_is_vector(foobar()))
  expect_false(obj_is_list(foobar()))
})

test_that("data frames and records are vectors", {
  expect_true(obj_is_vector(mtcars))
  expect_true(obj_is_vector(new_rcrd(list(x = 1, y = 2))))
})

test_that("non-vector base types are scalars", {
  expect_identical(vec_typeof(quote(foo)), "scalar")
  expect_identical(vec_typeof(pairlist("")), "scalar")
  expect_identical(vec_typeof(function() NULL), "scalar")
  expect_identical(vec_typeof(env()), "scalar")
  expect_identical(vec_typeof(quote(foo)), "scalar")
  expect_identical(vec_typeof(~foo), "scalar")
  expect_identical(vec_typeof(base::`{`), "scalar")
  expect_identical(vec_typeof(base::c), "scalar")
  expect_identical(vec_typeof(expression()), "scalar")

  expect_false(obj_is_vector(quote(foo)))
  expect_false(obj_is_vector(pairlist("")))
  expect_false(obj_is_vector(function() NULL))
  expect_false(obj_is_vector(env()))
  expect_false(obj_is_vector(~foo))
  expect_false(obj_is_vector(base::`{`))
  expect_false(obj_is_vector(base::c))
  expect_false(obj_is_vector(expression()))

  expect_false(vec_is(quote(foo)))
  expect_false(vec_is(pairlist("")))
  expect_false(vec_is(function() NULL))
  expect_false(vec_is(env()))
  expect_false(vec_is(~foo))
  expect_false(vec_is(base::`{`))
  expect_false(vec_is(base::c))
  expect_false(vec_is(expression()))

  expect_error(vec_assert(quote(foo)), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(pairlist("")), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(function() NULL), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(env()), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(~foo), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(base::`{`), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(base::c), class = "vctrs_error_scalar_type")
  expect_error(vec_assert(expression()), class = "vctrs_error_scalar_type")
})

test_that("non-vector types can be proxied", {
  x <- new_proxy(1:3)

  expect_identical(vec_typeof(x), "scalar")
  expect_false(obj_is_vector(x))
  expect_false(vec_is(x))
  expect_error(vec_assert(x), class = "vctrs_error_scalar_type")

  local_env_proxy()

  expect_identical(vec_typeof(x), "integer")
  expect_true(obj_is_vector(x))
  expect_true(vec_is(x))
  expect_error(regexp = NA, vec_assert(x))
})

test_that("obj_check_vector() is silent on vectors", {
  expect_null(obj_check_vector(1))
  expect_null(obj_check_vector(data_frame()))
})

test_that("obj_check_vector() errors on scalars", {
  expect_snapshot(error = TRUE, {
    obj_check_vector(quote(foo))
  })
  expect_snapshot(error = TRUE, {
    obj_check_vector(foobar())
  })
})

test_that("obj_check_vector() error respects `arg` and `call`", {
  my_check_vector <- function(foo) {
    obj_check_vector(foo)
  }

  expect_snapshot(error = TRUE, {
    my_check_vector(foobar())
  })
})

test_that("vec_assert() uses friendly type in error messages", {
   # Friendly type will be generated in rlang in the future. Upstream
   # changes should not cause CRAN failures.
  skip_on_cran()
  expect_error(vec_assert(function() NULL), class = "vctrs_error_scalar_type")
})

test_that("vec_typeof() handles all types", {
  for (i in seq_along(empty_types)) {
    expect_identical(vec_typeof(!!empty_types[[i]]), !!names(empty_types)[[i]])
  }
})

test_that("bare prototypes don't act as partial types", {
  expect_false(vec_is(foobar(1), dbl()))
  expect_error(vec_assert(foobar(1), dbl()), class = "vctrs_error_assert_ptype")
})

test_that("data frames are always classified as such even when dispatch is off", {
  expect_identical(vec_typeof_bare(mtcars), "dataframe")
})

test_that("assertion is not applied on proxy", {
  local_methods(
    vec_proxy.vctrs_foobar = function(x, ...) unclass(x),
    vec_restore.vctrs_foobar = function(x, ...) foobar(x),
    `[.vctrs_foobar` = function(x, i) vec_slice(x, i)
  )
  x <- foobar(list())

  expect_true(vec_is(x, x))
  expect_false(vec_is(x, list()))

  expect_error(vec_assert(x, list()), class = "vctrs_error_assert_ptype")
  expect_error(vec_assert(x, x), regexp = NA)
})

test_that("attributes of unclassed vectors are asserted", {
  x <- structure(FALSE, foo = "bar")
  y <- structure(TRUE, foo = "bar")
  expect_false(vec_is(x, FALSE))
  expect_false(vec_is(FALSE, x))
  expect_true(vec_is(y, x))
  expect_true(vec_is(x, y))
})

test_that("unspecified is finalised before assertion", {
  expect_true(vec_is(NA, TRUE))
  expect_true(vec_is(data.frame(x = NA), data.frame(x = lgl())))

  expect_error(regexp = NA, vec_assert(NA, TRUE))
  expect_error(regexp = NA, vec_assert(data.frame(x = NA), data.frame(x = lgl())))
})

test_that("assertion failures are explained", {
  local_no_stringsAsFactors()
  local_options(rlang_backtrace_on_error = "none")

  expect_snapshot(error = TRUE, vec_assert(lgl(), chr()))

  expect_snapshot(error = TRUE, vec_assert(lgl(), factor()))
  expect_snapshot(error = TRUE, vec_assert(lgl(), factor(levels = "foo")))
  expect_snapshot(error = TRUE, vec_assert(factor(levels = "bar"), factor(levels = "foo")))

  expect_snapshot(error = TRUE, vec_assert(factor(), chr()))

  expect_snapshot(error = TRUE, vec_assert(lgl(), data.frame()))
  expect_snapshot(error = TRUE, vec_assert(lgl(), data.frame(x = 1)))
  expect_snapshot(error = TRUE, vec_assert(lgl(), data.frame(x = 1, y = 2)))

  expect_snapshot(error = TRUE, vec_assert(data.frame(), chr()))

  expect_snapshot(error = TRUE, vec_assert(data.frame(x = 1), chr()))
  expect_snapshot(error = TRUE, vec_assert(data.frame(x = 1), data.frame(x = "foo")))
  expect_snapshot(error = TRUE, vec_assert(data.frame(x = 1), data.frame(x = "foo", y = 2)))

  expect_snapshot(error = TRUE, vec_assert(data.frame(x = 1, y = 2), chr()))

  expect_snapshot(error = TRUE, vec_assert(data.frame(x = 1, y = 2), data.frame(x = "foo")))
  expect_snapshot(error = TRUE, vec_assert(data.frame(x = 1, y = 2), data.frame(x = "foo", y = 2)))
})

test_that("vec_assert() validates `size` (#1470)", {
  expect_snapshot({
    (expect_error(vec_assert(1, size = c(2, 3))))
    (expect_error(vec_assert(1, size = 1.5)))
    (expect_error(vec_assert(1, size = "x")))
  })
})

test_that("NULL is not a vector", {
  expect_false(obj_is_vector(NULL))
  expect_false(vec_is(NULL))
})

test_that("names and row names do not influence type identity (#707)", {
  expect_true(vec_is(c(a = TRUE), logical()))
  expect_true(vec_is(TRUE, c(a = TRUE)))
  expect_true(vec_is(structure(mtcars, row.names = 1:32), mtcars))
  expect_true(vec_is(mtcars, structure(mtcars, row.names = 1:32)))
})

# vec_check_size --------------------------------------------------------

test_that("vec_check_size() is silent if the size is right", {
  expect_null(vec_check_size(1:5, size = 5L))
  expect_null(vec_check_size(data_frame(.size = 10L), size = 10L))
})

test_that("vec_check_size() errors on the wrong size", {
  expect_snapshot(error = TRUE, {
    vec_check_size(1:5, size = 1L)
  })
  expect_snapshot(error = TRUE, {
    vec_check_size(1:5, size = 10L)
  })
})

test_that("vec_check_size() errors on scalars", {
  expect_snapshot(error = TRUE, {
    vec_check_size(quote(foo), size = 1L)
  })
  expect_snapshot(error = TRUE, {
    vec_check_size(foobar(), size = 1L)
  })
})

test_that("vec_check_size() error respects `arg` and `call`", {
  my_check_size <- function(foo, size) {
    vec_check_size(foo, size)
  }

  expect_snapshot(error = TRUE, {
    my_check_size(1L, size = 5L)
  })
  expect_snapshot(error = TRUE, {
    my_check_size(foobar(), size = 5L)
  })
})

test_that("vec_check_size() validates `size`", {
  expect_snapshot(error = TRUE, {
    vec_check_size(1, size = "x")
  })
  expect_snapshot(error = TRUE, {
    vec_check_size(1, size = c(1L, 2L))
  })
  expect_snapshot(error = TRUE, {
    vec_check_size(1, size = 1.5)
  })
})

# obj_is_list -----------------------------------------------------------

test_that("bare lists are lists", {
  expect_true(obj_is_list(list()))
})

test_that("AsIs lists are lists (#1463)", {
  expect_true(obj_is_list(I(list())))
  expect_true(obj_is_list(I(list_of(1))))
  expect_false(obj_is_list(I(double())))
})

test_that("list_of are lists", {
  expect_true(obj_is_list(new_list_of()))
})

test_that("Vectors with a non-VECSXP type are not lists", {
  expect_false(obj_is_list(1))
  expect_false(obj_is_list("a"))
  expect_false(obj_is_list(quote(name)))
})

test_that("explicitly classed lists are lists", {
  x <- structure(list(), class = "list")

  expect_true(obj_is_list(x))
  expect_true(obj_is_list(subclass(x)))
})

test_that("explicit inheritance must be in the base class", {
  x <- structure(1:2, class = c("list", "foobar"))
  expect_false(obj_is_list(x))
})

test_that("POSIXlt are not considered a list", {
  expect_false(obj_is_list(as.POSIXlt(new_datetime())))
})

test_that("rcrd types are not lists", {
  expect_false(obj_is_list(new_rcrd(list(x = 1))))
})

test_that("scalars are not lists", {
  expect_false(obj_is_list(foobar()))
})

test_that("S3 types can't lie about their internal representation", {
  x <- structure(1:2, class = c("foobar", "list"))
  expect_false(obj_is_list(x))
})

test_that("data frames of all types are not lists", {
  expect_false(obj_is_list(data.frame()))
  expect_false(obj_is_list(subclass(data.frame())))
  expect_false(obj_is_list(tibble::tibble()))
})

test_that("S3 list with non-list proxy is still a list (#1208)", {
  x <- structure(list(), class = c("foobar", "list"))
  local_methods(vec_proxy.foobar = function(x) 1)
  # This used to be an error (#1003)
  # expect_error(obj_is_list(x), "`x` inherits")
  expect_true(obj_is_list(x))
})

test_that("list-rcrds with data frame proxies are considered lists (#1208)", {
  x <- structure(
    list(1:2, "x"),
    special = c("a", "b"),
    class = c("list_rcrd", "list")
  )

  local_methods(
    vec_proxy.list_rcrd = function(x) {
      special <- attr(x, "special")
      data <- unstructure(x)
      new_data_frame(list(data = data, special = special))
    }
  )

  expect_true(obj_is_list(x))
})

test_that("list_all_vectors() works", {
  expect_true(list_all_vectors(list(1)))
  expect_true(list_all_vectors(list_of(1)))
  expect_false(list_all_vectors(list(1, env())))
  expect_snapshot((expect_error(list_all_vectors(env()))))
})

test_that("obj_check_list() works", {
  expect_null(obj_check_list(list(1)))
  expect_null(obj_check_list(list_of(1)))
  expect_snapshot({
    my_function <- function(my_arg) obj_check_list(my_arg)
    (expect_error(my_function(env())))
  })
})

test_that("obj_check_list() uses a special error when `arg` is the empty string (#1604)", {
  expect_snapshot(error = TRUE, {
    obj_check_list(1, arg = "")
  })
})

test_that("obj_check_list() and list_check_all_vectors() work", {
  expect_null(list_check_all_vectors(list()))
  expect_null(list_check_all_vectors(list(1, mtcars)))
  expect_snapshot({
    my_function <- function(my_arg) list_check_all_vectors(my_arg)
    (expect_error(my_function(env())))
    (expect_error(my_function(list(1, env()))))
    (expect_error(my_function(list(1, name = env()))))
    (expect_error(my_function(list(1, foo = env()))))
  })
})

test_that("list_all_size() works", {
  expect_true(list_all_size(list(), 2))
  expect_true(list_all_size(list(integer()), 0))
  expect_true(list_all_size(list(NULL), 0))
  expect_true(list_all_size(list(1:2, 2:3), 2))

  expect_false(list_all_size(list(1:2, 1:3), 2))
  expect_false(list_all_size(list(NULL, 1:2), 2))

  expect_true(list_all_size(list_of(1:3, 2:4), 3))
  expect_false(list_all_size(list_of(1:3, 2:4), 4))
})

test_that("list_check_all_size() works", {
  expect_null(list_check_all_size(list(), 2))
  expect_null(list_check_all_size(list(integer()), 0))
  expect_null(list_check_all_size(list(NULL), 0))
  expect_null(list_check_all_size(list(1:2, 2:3), 2))

  expect_snapshot({
    my_function <- function(my_arg, size) list_check_all_size(my_arg, size)

    # Validates sizes
    (expect_error(list_check_all_size(list(1:2, 1:3), 2)))
    (expect_error(my_function(list(1:2, 1:3), 2)))

    # `NULL` is not ignored
    (expect_error(my_function(list(NULL, 1:2), 2)))
  })
})

test_that("list_all_size() and list_check_all_size() error on scalars", {
  x <- list(env())

  expect_snapshot({
    # Error considered internal to `list_all_size()`
    (expect_error(list_all_size(x, 2)))

    my_function <- function(my_arg, size) list_check_all_size(my_arg, size)
    (expect_error(my_function(x, 2)))
  })
})

test_that("list_all_size() and list_check_all_size() throw error using internal call on non-list input", {
  expect_snapshot({
    (expect_error(list_all_size(1, 2)))

    # `arg` and `call` are ignored
    (expect_error(list_check_all_size(1, 2, arg = "arg", call = call("foo"))))
  })
})

test_that("list_all_size() and list_check_all_size() validate `size`", {
  expect_snapshot({
    (expect_error(list_all_size(list(), size = "x")))
    (expect_error(list_check_all_size(list(), size = "x")))
  })
})

test_that("informative messages when 1d array doesn't match vector", {
  x <- array(1:3)
  expect_snapshot((expect_error(vec_assert(x, int()))))
})
