test_that("new_grouped_df can create alternative grouping structures (#3837)", {
  tbl <- new_grouped_df(
    tibble(x = rnorm(10)),
    groups = tibble(
      ".rows" := replicate(5, sample(1:10, replace = TRUE), simplify = FALSE)
    )
  )
  res <- summarise(tbl, x = mean(x))
  expect_equal(nrow(res), 5L)
})

test_that("new_grouped_df does not have rownames (#4173)", {
  tbl <- new_grouped_df(
    tibble(x = rnorm(10)),
    groups = tibble(
      ".rows" := replicate(5, sample(1:10, replace = TRUE), simplify = FALSE)
    )
  )
  expect_false(tibble::has_rownames(tbl))
})

test_that("[ method can remove grouping vars", {
  df <- tibble(x = 1, y = 2, z = 3)
  gf <- group_by(df, x, y)

  expect_equal(gf, gf)
  expect_equal(gf[1], group_by(df[1], x))
  expect_equal(gf[3], df[3])
})

test_that("[ method reuses group_data() if possible", {
  df <- tibble(x = 1, y = 2, z = 3)
  gf <- group_by(df, x, y)

  expect_true(rlang::is_reference(group_data(gf), group_data(gf[1:2])))
  expect_true(rlang::is_reference(group_data(gf), group_data(gf[, 1:2])))
})

test_that("[ supports drop=TRUE (#3714)", {
  df <- tibble(x = 1, y = 2)
  gf <- group_by(df, x)

  expect_type(gf[, "y", drop = TRUE], "double")
  expect_s3_class(gf[, c("x", "y"), drop = TRUE], "tbl_df")
})

test_that("$<-, [[<-, and [<- update grouping data if needed", {
  df <- tibble(x = 1, y = 2)
  gf <- group_by(df, x)

  # value has to be past the ellipsis in $<-()
  expect_equal(group_data(`$<-`(gf, "x", value = 2))$x, 2)
  expect_equal(group_data(`$<-`(gf, "y", value = 2))$x, 1)

  expect_equal(
    group_data({
      gf2 <- gf
      gf2[[1]] <- 3
      gf2
    })$x,
    3
  )
  expect_equal(group_data(`[<-`(gf, 1, "x", value = 4))$x, 4)
})

test_that("can remove grouping cols with subset assignment", {
  df <- tibble(x = 1, y = 2)
  gf1 <- gf2 <- gf3 <- group_by(df, x, y)

  gf1$x <- NULL
  gf2[["x"]] <- NULL
  gf3[, "x"] <- NULL

  expect_named(group_data(gf1), c("y", ".rows"))
  expect_named(group_data(gf2), c("y", ".rows"))
  expect_named(group_data(gf3), c("y", ".rows"))
})

test_that("names<- updates grouping data", {
  df <- tibble(x = 1, y = 2, z = 3)
  gf <- group_by(df, x, y)

  names(gf) <- c("z1", "z2", "z3")
  expect_named(group_data(gf), c("z1", "z2", ".rows"))

  names(gf)[1] <- c("Z1")
  expect_named(group_data(gf), c("Z1", "z2", ".rows"))
})

test_that("names<- doesn't modify group data if not necessary", {
  df <- tibble(x = 1, y = 2)
  gf1 <- gf2 <- group_by(df, x)
  expect_true(rlang::is_reference(group_data(gf1), group_data(gf2)))

  names(gf1) <- c("x", "Y")
  expect_true(rlang::is_reference(group_data(gf1), group_data(gf2)))
})

test_that("group order is maintained in grouped-df methods (#5040)", {
  gdf <- group_by(mtcars, cyl, am, vs)

  x <- gdf[0, ]
  expect_identical(group_vars(x), group_vars(gdf))

  x <- gdf
  x$am <- 1
  expect_identical(group_vars(x), group_vars(gdf))

  x <- gdf
  x["am"] <- 1
  expect_identical(group_vars(x), group_vars(gdf))

  x <- gdf
  x[["am"]] <- 1
  expect_identical(group_vars(x), group_vars(gdf))

  x <- gdf
  names <- names(x)
  names[9] <- "am2"
  names(x) <- names
  expect_identical(group_vars(x), group_vars(group_by(x, cyl, am2, vs)))
})

# validate ----------------------------------------------------------------

test_that("validate_grouped_df() gives useful errors", {
  df1 <- group_by(tibble(x = 1:4, g = rep(1:2, each = 2)), g)
  groups <- attr(df1, "groups")
  groups[[2]] <- 1:2
  attr(df1, "groups") <- groups

  df2 <- group_by(tibble(x = 1:4, g = rep(1:2, each = 2)), g)
  groups <- attr(df2, "groups")
  names(groups) <- c("g", "not.rows")
  attr(df2, "groups") <- groups

  df3 <- df2
  attr(df3, "groups") <- tibble()

  df4 <- df3
  attr(df4, "groups") <- NA

  df5 <- tibble(x = 1:4, g = rep(1:2, each = 2))
  attr(df5, "vars") <- "g"
  attr(df5, "class") <- c("grouped_df", "tbl_df", "tbl", "data.frame")

  df6 <- new_grouped_df(
    tibble(x = 1:10),
    groups = tibble(".rows" := list(1:5, -1L))
  )
  df7 <- df6
  attr(df7, "groups")$.rows <- list(11L)

  df8 <- df6
  attr(df8, "groups")$.rows <- list(0L)

  df10 <- df6
  attr(df10, "groups") <- tibble()

  df11 <- df6
  attr(df11, "groups") <- NULL

  expect_snapshot({
    # Invalid `groups` attribute
    (expect_error(validate_grouped_df(df1)))
    (expect_error(group_data(df1)))
    (expect_error(validate_grouped_df(df2)))
    (expect_error(validate_grouped_df(df2)))
    (expect_error(validate_grouped_df(df3)))
    (expect_error(validate_grouped_df(df4)))

    # Older style grouped_df
    (expect_error(validate_grouped_df(df5)))

    # validate_grouped_df(
    (expect_error(validate_grouped_df(df6, check_bounds = TRUE)))
    (expect_error(validate_grouped_df(df7, check_bounds = TRUE)))
    (expect_error(validate_grouped_df(df8, check_bounds = TRUE)))
    (expect_error(validate_grouped_df(df10)))
    (expect_error(validate_grouped_df(df11)))

    # new_grouped_df()
    (expect_error(
      new_grouped_df(
        tibble(x = 1:10),
        tibble(other = list(1:2))
      )
    ))
    (expect_error(new_grouped_df(10)))
  })
})

# compute_group ----------------------------------------------------------

test_that("helper gives meaningful error messages", {
  expect_snapshot({
    (expect_error(grouped_df(data.frame(x = 1), "y", FALSE)))
    (expect_error(grouped_df(data.frame(x = 1), 1)))
  })
})

test_that("NA and NaN are in separate groups at the end", {
  df <- tibble(x = c(NA, NaN, NA, 1))
  result <- compute_groups(df, "x")
  expect_identical(result$x, c(1, NaN, NA))
})

test_that("groups are ordered in the C locale", {
  df <- tibble(x = c("a", "A", "Z", "b"))
  result <- compute_groups(df, "x")
  expect_identical(result$x, c("A", "Z", "a", "b"))
})

test_that("using the deprecated global option `dplyr.legacy_locale` forces the system locale", {
  skip_if_not(has_collate_locale("en_US"), message = "Can't use 'en_US' locale")

  local_options(dplyr.legacy_locale = TRUE)
  withr::local_collate("en_US")

  df <- tibble(x = c("a", "A", "Z", "b"))

  # Should get deprecation warning about `dplyr.legacy_locale`
  expect_snapshot({
    result <- compute_groups(df, "x")
  })

  expect_identical(result$x, c("a", "A", "b", "Z"))

  # Confirming that the deprecation warning also shows up in `group_by()` itself
  expect_snapshot({
    result <- group_by(df, x)
  })

  expect_identical(group_data(result)$x, c("a", "A", "b", "Z"))
})
