# Parameters --------------------------------------------------------------

test_that("layer() checks its input", {
  expect_snapshot_error(layer(stat = "identity", position = "identity"))
  expect_snapshot_error(layer(geom = "point", position = "identity"))
  expect_snapshot_error(layer(geom = "point", stat = "identity"))

  expect_snapshot_error(layer("point", "identity", mapping = 1:4, position = "identity"))
  expect_snapshot_error(layer("point", "identity", mapping = ggplot(), position = "identity"))

  expect_snapshot_error(validate_subclass("test", "geom"))
  expect_snapshot_error(validate_subclass(environment(), "geom"))

  geom_foo <- function(...) stop("This function is unconstructable.")
  expect_snapshot_error(layer("foo", "identity", position = "identity"))
})

test_that("aesthetics go in aes_params", {
  l <- geom_point(size = "red")
  expect_equal(l$aes_params, list(size = "red"))
})

test_that("unknown params create warning", {
  expect_snapshot_warning(geom_point(blah = "red"))
})

test_that("unknown aesthetics create warning", {
  expect_snapshot_warning(geom_point(aes(blah = "red")))
})

test_that("empty aesthetics create warning", {
  p <- ggplot(mtcars) + geom_point(aes(disp, mpg), fill = NULL, shape = character())
  expect_snapshot_warning(ggplot_build(p))
})

test_that("invalid aesthetics throws errors", {
  # We want to test error and ignore the scale search message
  suppressMessages({
    p <- ggplot(mtcars) + geom_point(aes(disp, mpg, fill = data))
    expect_snapshot_error(ggplot_build(p))
    p <- ggplot(mtcars) + geom_point(aes(disp, mpg, fill = after_stat(data)))
    expect_snapshot_error(ggplot_build(p))
  })
})

test_that("unknown NULL aesthetic doesn't create warning (#1909)", {
  expect_silent(geom_point(aes(blah = NULL)))
})

test_that("column vectors are allowed (#2609)", {
  df <- data_frame(x = 1:10)
  df$y <- scale(1:10) # Returns a column vector
  p <- ggplot(df, aes(x, y))
  expect_s3_class(get_layer_data(p), "data.frame")
})

test_that("missing aesthetics trigger informative error", {
  df <- data_frame(x = 1:10)
  expect_snapshot(
    ggplot_build(ggplot(df) + geom_line()),
    error = TRUE
  )
  expect_snapshot(
    ggplot_build(ggplot(df) + geom_col()),
    error = TRUE
  )
})

test_that("function aesthetics are wrapped with after_stat()", {
  df <- data_frame(x = 1:10)
  suppressMessages(
    expect_snapshot_error(
      ggplot_build(
        ggplot(df, aes(colour = density, fill = density)) + geom_point()
      )
    )
  )
})

test_that("computed stats are in appropriate layer", {
  df <- data_frame(x = 1:10)
  expect_snapshot_error(
    ggplot_build(ggplot(df, aes(colour = after_stat(density), fill = after_stat(density))) + geom_point())
  )
})

test_that("if an aes is mapped to a function that returns NULL, it is removed", {
  df <- data_frame(x = 1:10)
  null <- function(...) NULL
  p <- cdata(ggplot(df, aes(x, null())))
  expect_named(p[[1]], c("x", "PANEL", "group"))
})

test_that("layers are stateless except for the computed params", {
  df <- data.frame(x = 1:10, y = 1:10)
  p <- ggplot(df) +
    geom_col(aes(x = x, y = y), width = 0.8, fill = "red")
  col_layer <- as.list(p@layers[[1]])
  stateless_names <- setdiff(names(col_layer), c("computed_geom_params", "computed_stat_params", "computed_mapping"))
  invisible(ggplotGrob(p))
  expect_identical(as.list(p@layers[[1]])[stateless_names], col_layer[stateless_names])
})

test_that("inherit.aes works", {
  df <- data.frame(x = 1:10, y = 1:10)
  p1 <- ggplot(df, aes(y = y)) +
    geom_col(aes(x = x), inherit.aes = TRUE)
  p2 <- ggplot(df, aes(colour = y)) +
    geom_col(aes(x = x, y = y), inherit.aes = FALSE)
  invisible(ggplotGrob(p1))
  invisible(ggplotGrob(p2))
  expect_identical(p1@layers[[1]]$computed_mapping, p2@layers[[1]]$computed_mapping)
})

test_that("retransform works on computed aesthetics in `map_statistic`", {
  df <- data.frame(x = rep(c(1,2), c(9, 25)))
  p <- ggplot(df, aes(x)) + geom_bar() + scale_y_sqrt()
  expect_equal(get_layer_data(p)$y, c(3, 5))

  # To double check: should be original values when `retransform = FALSE`
  parent <- p@layers[[1]]$stat
  p@layers[[1]]$stat <- ggproto(NULL, parent, retransform = FALSE)
  expect_equal(get_layer_data(p)$y, c(9, 25))
})

test_that("layer reports the error with correct index etc", {
  p <- ggplot(mtcars) + geom_linerange(aes(disp, mpg), ymin = 2)

  expect_snapshot_error(ggplotGrob(p))

  p <- ggplot(
    data_frame(x = "one value", y = 3, value = 4:6),
    aes(x, ymin = 0, lower = 1, middle = y, upper = value, ymax = 10)
  ) +
    geom_point(aes(x = x, y = y), inherit.aes = FALSE) +
    geom_boxplot(stat = "identity")

  expect_snapshot_error(ggplotGrob(p))
})

test_that("layer warns for constant aesthetics", {
  p <- ggplot(mtcars, aes(x = seq_along(mpg))) + geom_point(aes(y = 2))
  expect_silent(ggplot_build(p))

  p <- ggplot(mtcars, aes(x = 1)) + geom_point(aes(y = 2))
  expect_snapshot_warning(ggplot_build(p))
})

test_that("layer names can be resolved", {

  p <- ggplot() + geom_point() + geom_point()
  expect_named(p@layers, c("geom_point", "geom_point...2"))

  p <- ggplot() + geom_point(name = "foo") + geom_point(name = "bar")
  expect_named(p@layers, c("foo", "bar"))

  l <- geom_point(name = "foobar")
  expect_snapshot(p + l + l, error = TRUE)
})

test_that("validate_subclass can resolve classes via constructors", {

  env <- new_environment(list(
    geom_foobar = geom_point,
    stat_foobar = stat_boxplot,
    position_foobar = position_nudge,
    guide_foobar = guide_axis_theta
  ))

  expect_s3_class(validate_subclass("foobar", "Geom", env = env), "GeomPoint")
  expect_s3_class(validate_subclass("foobar", "Stat", env = env), "StatBoxplot")
  expect_s3_class(validate_subclass("foobar", "Position", env = env), "PositionNudge")
  expect_s3_class(validate_subclass("foobar", "Guide", env = env), "GuideAxisTheta")

})

test_that("attributes on layer data are preserved", {
  # This is a good layer for testing because:
  # * It needs to compute a statistic at the group level
  # * It needs to setup data to reshape x/y/width/height into xmin/xmax/ymin/ymax
  # * It needs to use a position adjustment
  # * It has an `after_stat()` so it enters the map_statistic method
  old <- stat_summary(
    aes(fill = after_stat(y)),
    fun = mean, geom = "col", position = "dodge"
  )
  # We modify the compute aesthetics method to append a test attribute
  new <- ggproto(NULL, old, compute_aesthetics = function(self, data, plot) {
    data <- ggproto_parent(old, self)$compute_aesthetics(data, plot)
    attr(data, "test") <- "preserve me"
    data
  })
  # At the end of plot building, we want to retrieve that metric
  ld <- layer_data(
    ggplot(mpg, aes(drv, hwy, colour = factor(year))) + new + facet_grid(~year) +
      scale_y_sqrt()
  )
  expect_equal(attr(ld, "test"), "preserve me")
})

# Data extraction ---------------------------------------------------------

test_that("AsIs data passes unmodified", {
  p <- ggplot() + geom_blank(aes(x = 1:2, y = 1:2))
  ld <- get_layer_data(p + geom_point(aes(x = I(0.5), y = I(0.5))), 2)
  expect_s3_class(ld$x, "AsIs")
  expect_equal(ld$y, I(0.5))
  ld <- get_layer_data(p + geom_point(x = I(0.5), y = I(0.5), data = mtcars), 2)
  expect_s3_class(ld$x, "AsIs")
  expect_equal(ld$y[1], I(0.5))
  ld <- get_layer_data(p + annotate("point", x = I(0.5), y = I(0.5)), 2)
  expect_s3_class(ld$x, "AsIs")
  expect_equal(ld$y, I(0.5))
})

test_that("layer_data returns a data.frame", {
  l <- geom_point()
  expect_equal(l$layer_data(mtcars), unrowname(mtcars))
  l <- geom_point(data = head(mtcars))
  expect_equal(l$layer_data(mtcars), head(unrowname(mtcars)))
  l <- geom_point(data = head)
  expect_equal(l$layer_data(mtcars), head(unrowname(mtcars)))
  l <- geom_point(data = ~ head(., 10))
  expect_equal(l$layer_data(mtcars), head(unrowname(mtcars), 10))
  l <- geom_point(data = nrow)
  expect_snapshot_error(l$layer_data(mtcars))
})

test_that("data.frames and matrix aesthetics survive the build stage", {
  df <- data_frame0(
    x = 1:2,
    g = matrix(1:4, 2),
    f = data_frame0(a = 1:2, b = c("c", "d"))
  )

  p <- layer_data(
    ggplot(df, aes(x, x, colour = g, shape = f)) +
      geom_point() +
      scale_colour_identity() +
      scale_shape_identity()
  )
  expect_vector(p$colour, matrix(NA_integer_, nrow = 0, ncol = 2), size = 2)
  expect_vector(p$shape,  data_frame0(a = integer(), b = character()), size = 2)
})
