test_that("expands the first column slowest by default", {
  x <- 1:4
  y <- 1:3
  z <- 1:2

  expect_identical(
    vec_expand_grid(x = x, y = y, z = z),
    data_frame(
      x = vec_rep(vec_rep_each(x, times = 6), times = 1),
      y = vec_rep(vec_rep_each(y, times = 2), times = 4),
      z = vec_rep(vec_rep_each(z, times = 1), times = 12)
    )
  )
})

test_that("can expand the first column fastest with `.vary`", {
  x <- 1:4
  y <- 1:3
  z <- 1:2

  expect_identical(
    vec_expand_grid(x = x, y = y, z = z, .vary = "fastest"),
    data_frame(
      x = vec_rep(vec_rep_each(x, times = 1), times = 6),
      y = vec_rep(vec_rep_each(y, times = 4), times = 2),
      z = vec_rep(vec_rep_each(z, times = 12), times = 1)
    )
  )
})

test_that("size 0 elements force a size 0 result", {
  expect_identical(
    vec_expand_grid(x = 1:3, y = integer(), z = 1:2),
    data_frame(x = integer(), y = integer(), z = integer())
  )

  expect_identical(
    vec_expand_grid(x = integer()),
    data_frame(x = integer())
  )
})

test_that("returns 1 row and 0 cols with no input", {
  # Because `prod(integer()) == 1L`
  expect_identical(vec_expand_grid(), data_frame(.size = 1L))
})

test_that("drops `NULL` values", {
  expect_identical(
    vec_expand_grid(NULL, NULL),
    vec_expand_grid()
  )

  # And that happens before all names checks
  expect_identical(
    vec_expand_grid(x = 1:2, x = NULL, y = 1:3, NULL),
    vec_expand_grid(x = 1:2, y = 1:3)
  )
})

test_that("works with data frame inputs", {
  x <- data_frame(a = 1:2, b = 2:3)
  y <- 1:3

  expect_identical(
    vec_expand_grid(x = x, y = y),
    data_frame(
      x = vec_rep(vec_rep_each(x, times = 3), times = 1),
      y = vec_rep(vec_rep_each(y, times = 1), times = 2),
    )
  )
})

test_that("`.name_repair` isn't affected by `.vary`", {
  expect <- vec_as_names(c("a", "b", "a", "z"), repair = "unique_quiet")

  expect_named(
    vec_expand_grid(
      a = 1,
      b = 2,
      a = 3,
      z = 4,
      .vary = "slowest",
      .name_repair = "unique_quiet"
    ),
    expect
  )
  expect_named(
    vec_expand_grid(
      a = 1,
      b = 2,
      a = 3,
      z = 4,
      .vary = "fastest",
      .name_repair = "unique_quiet"
    ),
    expect
  )
})

test_that("can use `.name_repair`", {
  expect_identical(
    vec_expand_grid(a = 1:2, a = 2:3, .name_repair = "minimal"),
    data_frame(
      a = c(1L, 1L, 2L, 2L),
      a = c(2L, 3L, 2L, 3L),
      .name_repair = "minimal"
    )
  )
})

test_that("inputs must be named", {
  expect_snapshot(error = TRUE, {
    vec_expand_grid(1)
  })
  expect_snapshot(error = TRUE, {
    vec_expand_grid(x = 1, 2, y = 3)
  })
})

test_that("catches duplicate names by default", {
  expect_snapshot(error = TRUE, {
    vec_expand_grid(a = 1, a = 2)
  })
})

test_that("errors on non vectors and mentions the element name", {
  expect_snapshot(error = TRUE, {
    vec_expand_grid(y = environment())
  })
})

test_that("can adjust the `.error_call`", {
  my_expand_grid <- function() {
    vec_expand_grid(x = environment(), .error_call = current_env())
  }

  expect_snapshot(error = TRUE, {
    my_expand_grid()
  })
})

test_that("errors nicely when expansion results in a size larger than `R_len_t`", {
  # Windows 32-bit doesn't support long vectors of this size, and the
  # intermediate `r_ssize` will be too large
  skip_if(.Machine$sizeof.pointer < 8L, message = "No long vector support")

  x <- seq_len((2^31 - 1) / 2)
  y <- 1:3

  expect_snapshot(error = TRUE, {
    vec_expand_grid(x = x, y = y)
  })
})

test_that("errors nicely when expansion results in a size larger than `R_xlen_t`", {
  # Windows 32-bit doesn't support long vectors of this size, and the
  # intermediate `r_ssize` will be too large
  skip_if(.Machine$sizeof.pointer < 8L, message = "No long vector support")

  x <- seq_len(2^31 - 1)

  expect_snapshot(error = TRUE, transform = scrub_internal_error_line_number, {
    vec_expand_grid(x = x, y = x)
  })
})

test_that("validates `.vary`", {
  expect_snapshot(error = TRUE, {
    vec_expand_grid(.vary = 1)
  })
  expect_snapshot(error = TRUE, {
    vec_expand_grid(.vary = "x")
  })
})
