test_that("bind_rows() handles simple inputs", {
  df1 <- tibble(x = 1:2, y = letters[1:2])
  df2 <- tibble(x = 3:4, y = letters[3:4])

  out <- bind_rows(df1, df2)
  expect_equal(out, tibble(x = 1:4, y = letters[1:4]))
})

test_that("bind_rows() reorders columns to match first df", {
  df1 <- tibble(x = 1, y = 2)
  df2 <- tibble(y = 1, x = 2)

  expect_named(bind_rows(df1, df2), c("x", "y"))
})

test_that("bind_rows() returns union of columns", {
  df1 <- tibble(x = 1)
  df2 <- tibble(y = 2)
  out <- bind_rows(df1, df2)

  expect_equal(out, tibble(x = c(1, NA), y = c(NA, 2)))
})

test_that("bind_rows() handles zero column data frames (#2175)", {
  df1 <- tibble(.rows = 1)
  df2 <- tibble(x = 1)
  out <- bind_rows(df1, df2)

  expect_equal(out, tibble(x = c(NA, 1)))
})

test_that("bind_rows() handles zero row data frames (#597)", {
  df1 <- tibble(x = numeric())
  df2 <- tibble(y = 1)

  out <- bind_rows(df1, df2)
  expect_equal(out, tibble(x = NA_real_, y = 1))
})

test_that("bind_rows() ignores NULL (#2056)", {
  df <- tibble(a = 1)

  expect_equal(bind_rows(df, NULL), df)
  expect_equal(bind_rows(list(df, NULL)), df)
})

test_that("bind_rows() creates a column of identifiers (#1337)", {
  df1 <- tibble(x = 1:2)
  df2 <- tibble(x = 3)

  # with
  out <- bind_rows(a = df1, b = df2, .id = "id")
  expect_equal(out, tibble(id = c("a", "a", "b"), x = 1:3))

  out <- bind_rows(list(a = df1, b = df2), .id = "id")
  expect_equal(out, tibble(id = c("a", "a", "b"), x = 1:3))

  # or without names
  out <- bind_rows(df1, df2, .id = "id")
  expect_equal(out, tibble(id = c("1", "1", "2"), x = 1:3))
})

test_that("bind_rows deduplicates row names", {
  df1 <- data.frame(x = 1:2, row.names = c("a", "b"))
  df2 <- data.frame(x = 3:4, row.names = c("a", "c"))
  out <- bind_rows(df1, df2)

  expect_equal(rownames(out), c("a...1", "b", "a...3", "c"))
})

test_that("bind_rows respects the drop attribute of grouped df", {
  df <- tibble(
    e = 1,
    f = factor(c(1, 1, 2, 2), levels = 1:3),
    g = c(1, 1, 2, 2),
    x = c(1, 2, 1, 4)
  )
  df <- group_by(df, e, f, g, .drop = FALSE)

  gg <- bind_rows(df, df)
  expect_equal(group_size(gg), c(4L, 4L, 0L))
})

# bind_rows() magic ---------------------------------------------------

test_that("bind_rows() handles lists of data frames #1389", {
  df <- tibble(x = 1)

  res <- bind_rows(list(df, df), list(df, df))
  expect_equal(nrow(res), 4)
})

test_that("bind_rows() ignores empty lists (#2826)", {
  df <- tibble(x = 1:10)
  expect_equal(bind_rows(list(df, list())), df)
})

test_that("bind_rows() accepts lists of dataframe-like lists as first argument", {
  ll <- list(a = 1, b = 2)
  expect_equal(bind_rows(list(ll)), tibble(a = 1, b = 2))
  expect_equal(bind_rows(list(ll, ll)), tibble(a = c(1, 1), b = c(2, 2)))
})

test_that("bind_rows() can handle lists (#1104)", {
  ll <- list(list(x = 1, y = "a"), list(x = 2, y = "b"))
  out <- bind_rows(ll)
  expect_equal(out, tibble(x = c(1, 2), y = c("a", "b")))

  out <- bind_rows(ll[[1]], ll[2])
  expect_equal(out, tibble(x = c(1, 2), y = c("a", "b")))
})

test_that("bind_rows() handles 0-length named list (#1515)", {
  x <- set_names(list())
  expect_equal(bind_rows(x), tibble())
})

test_that("bind_rows() handles tibbles + vectors", {
  out <- bind_rows(
    tibble(a = 1, b = 2),
    c(a = 3, b = 4)
  )
  expect_equal(out, tibble(a = c(1, 3), b = c(2, 4)))

  out <- bind_rows(
    a = c(a = 1, b = 2),
    b = c(a = 3, b = 4),
    .id = "id"
  )
  expect_equal(out, tibble(id = c("a", "b"), a = c(1, 3), b = c(2, 4)))
})

test_that("bind_rows() only flattens S3 lists that inherit from list (#3924)", {
  df <- data.frame(x = 1, y = 2)

  lst1 <- structure(list(df, df, df), class = "special_lst")
  expect_snapshot(bind_rows(lst1), error = TRUE)

  lst2 <- structure(list(df, df, df), class = c("special_lst", "list"))
  expect_equal(bind_rows(lst2), bind_rows(df, df, df))
})

test_that("bind_rows() handles named list", {
  x <- list(x = 1, y = 2, z = 3)
  expect_equal(bind_rows(x), tibble(x = 1, y = 2, z = 3))
})

test_that("bind_rows() handles empty names in a list (#7100)", {
  x <- rep(list(data.frame(x = 1)), times = 5)
  names(x) <- paste0("id_", 1:5)
  names(x)[c(3, 5)] <- NA_character_
  x <- bind_rows(x, .id = "id")

  # If names are missing, bind_rows will replace with index
  expect_identical(x$id, c("id_1", "id_2", "3", "id_4", "5"))
})

test_that("bind_rows() validates lists (#5417)", {
  out <- bind_rows(list(x = 1), list(x = 1, y = 1:2))
  expect_equal(out, tibble(x = c(1, 1, 1), y = c(NA, 1:2)))

  expect_snapshot(bind_rows(list(x = 1), list(x = 1:3, y = 1:2)), error = TRUE)
})

test_that("bind_rows() handles missing, null, and empty elements (#5429)", {
  x <- list(a = NULL, b = NULL)
  y <- list(a = "B", b = 2)
  l <- list(x, y)
  expect_identical(
    bind_rows(l),
    tibble(a = "B", b = 2)
  )

  x <- list(a = NULL, b = 1)
  y <- list(a = "B", b = 2)
  l <- list(x, y)
  expect_identical(
    bind_rows(l),
    tibble(b = c(1, 2), a = c(NA, "B"))
  )

  x <- list(a = character(0), b = 1)
  y <- list(a = "B", b = 2)
  l <- list(x, y)
  expect_identical(
    bind_rows(l),
    tibble(a = "B", b = 2)
  )
})

test_that("bind_rows(.id= NULL) does not set names (#5089)", {
  out <- bind_rows(list(a = tibble(x = 1:2)))
  expect_equal(attr(out, "row.names"), 1:2)

  out <- bind_rows(x = c(a = 1))
  expect_identical(attr(out, "row.names"), 1L)
})

# Column coercion --------------------------------------------------------------

test_that("bind_rows() promotes integer to numeric", {
  df1 <- tibble(a = 1L, b = 1L)
  df2 <- tibble(a = 1, b = 1L)

  res <- bind_rows(df1, df2)
  expect_type(res$a, "double")
  expect_type(res$b, "integer")
})

test_that("bind_rows() coerces factor when levels don't match", {
  df1 <- data.frame(a = factor("a"))
  df2 <- data.frame(a = factor("b"))

  res <- bind_rows(df1, df2)
  expect_equal(res$a, factor(c("a", "b")))
})

test_that("bind_rows() handles complex. #933", {
  df1 <- tibble(x = 1 + 1i)
  df2 <- tibble(x = 2 + 1i)

  out <- bind_rows(df1, df2)
  expect_equal(out, tibble(x = c(1 + 1i, 2 + 1i)))
})

test_that("bind_rows() handles POSIXct (#1125)", {
  df1 <- data.frame(date = as.POSIXct(NA))
  df2 <- data.frame(date = as.POSIXct("2015-05-05"))
  res <- bind_rows(df1, df2)
  expect_equal(nrow(res), 2L)
  expect_true(is.na(res$date[1]))
})

test_that("bind_rows() accepts data frame columns (#2015)", {
  df1 <- tibble(x = 1, y = tibble(a = 1, b = 1))
  df2 <- tibble(x = 2, y = tibble(a = 2, b = 2))

  out <- bind_rows(df1, df2)
  expect_equal(out, tibble(x = 1:2, y = tibble(a = 1:2, b = 1:2)))
})

test_that("bind_rows() accepts difftime objects", {
  df1 <- data.frame(x = as.difftime(1, units = "hours"))
  df2 <- data.frame(x = as.difftime(1, units = "mins"))
  res <- bind_rows(df1, df2)
  expect_equal(res$x, as.difftime(c(3600, 60), units = "secs"))
})

# Errors ------------------------------------------------------------

test_that("bind_rows() give informative errors", {
  expect_snapshot({
    "invalid .id"
    df1 <- tibble(x = 1:3)
    df2 <- tibble(x = 4:6)
    (expect_error(bind_rows(df1, df2, .id = 5)))

    "invalid type"
    ll <- list(tibble(a = 1:5), env(a = 1))
    (expect_error(bind_rows(ll)))

    df1 <- tibble(a = factor("a"))
    df2 <- tibble(a = 1L)
    (expect_error(bind_rows(df1, df2)))

    "unnamed vectors"
    (expect_error(bind_rows(1:2)))
  })
})
