# ------------------------------------------------------------------------------
# pmap()

furrr_test_that("future_pmap() matches pmap() for simple cases", {
  expect_identical(
    future_pmap(list(1:3, 4:6, 7:9), ~ .x + .y + ..3),
    pmap(list(1:3, 4:6, 7:9), ~ .x + .y + ..3)
  )
})

furrr_test_that("names of `.x` are retained", {
  x <- c(a = 1, b = 2)
  y <- c(c = 1, d = 2)
  expect_named(future_pmap(list(x, y), ~1), c("a", "b"))
})

furrr_test_that("named empty input makes named empty output", {
  x <- set_names(list(), character())
  expect_named(future_pmap(list(x, x), ~.x), character())
})

# ------------------------------------------------------------------------------
# atomic variants

furrr_test_that("future_pmap_dbl() works", {
  x <- c(1, 2, 3)
  y <- c(4, 5, 6)

  expect_identical(
    future_pmap_dbl(list(x, y), ~ .x + .y),
    pmap_dbl(list(x, y), ~ .x + .y)
  )
})

furrr_test_that("future_pmap_int() works", {
  x <- c(1L, 2L, 3L)
  y <- c(4L, 5L, 6L)

  expect_identical(
    future_pmap_int(list(x, y), ~ .x + .y),
    pmap_int(list(x, y), ~ .x + .y)
  )
})

furrr_test_that("future_pmap_lgl() works", {
  x <- c(TRUE, FALSE, TRUE)
  y <- c(FALSE, TRUE, TRUE)

  expect_identical(
    future_pmap_lgl(list(x, y), ~ .x || .y),
    pmap_lgl(list(x, y), ~ .x || .y)
  )
})

furrr_test_that("future_pmap_chr() works", {
  x <- c("a", "b", "c")
  y <- c("d", "e", "f")

  expect_identical(
    future_pmap_chr(list(x, y), ~.y),
    pmap_chr(list(x, y), ~.y)
  )
})

furrr_test_that("future_pmap_vec() works", {
  x <- as.Date(c("2020-01-01", "2020-01-02", "2020-01-03"))
  y <- 1:3

  expect_identical(
    future_pmap_vec(list(x, y), ~.x),
    pmap_vec(list(x, y), ~.x)
  )
  expect_identical(
    future_pmap_vec(list(x, y), ~1, .ptype = integer()),
    pmap_vec(list(x, y), ~1, .ptype = integer())
  )

  expect_identical(
    future_pmap_vec(list(integer(), integer()), identity),
    pmap_vec(list(integer(), integer()), identity)
  )
  expect_identical(
    future_pmap_vec(
      list(set_names(integer(), character()), integer()),
      identity
    ),
    pmap_vec(
      list(set_names(integer(), character()), integer()),
      identity
    )
  )

  # Vector error
  expect_snapshot(error = TRUE, {
    future_pmap_vec(list(1:2, 1:2), ~NULL)
  })
  # Size error
  expect_snapshot(error = TRUE, {
    future_pmap_vec(list(1:2, 1:2), ~ 1:2)
  })
  # Type error
  expect_snapshot(error = TRUE, {
    future_pmap_vec(list(1:2, 1:2), ~ if (.x == 1L) 1 else "x")
  })
})

furrr_test_that("names of `.x` are retained", {
  x <- c(a = 1, b = 2)
  y <- c(c = 1, d = 2)
  expect_named(future_pmap_dbl(list(x, y), ~1), c("a", "b"))

  x <- c(a = as.Date("2020-01-01"), b = as.Date("2020-01-02"))
  y <- c(c = 1, d = 2)
  expect_named(future_pmap_vec(list(x, y), ~1), c("a", "b"))
})

# ------------------------------------------------------------------------------
# data frame variants

furrr_test_that("future_pmap_dfr() works", {
  x <- c("a", "b", "c")
  y <- c("d", "e", "f")

  expect_identical(
    future_pmap_dfr(list(x, y), ~ data.frame(x = .x, y = .y)),
    pmap_dfr(list(x, y), ~ data.frame(x = .x, y = .y))
  )
})

furrr_test_that("future_pmap_dfc() works", {
  x <- c("a", "b", "c")
  y <- c("d", "e", "f")

  expect_identical(
    future_pmap_dfc(list(x, y), ~ as.data.frame(set_names(list(.x), .y))),
    pmap_dfc(list(x, y), ~ as.data.frame(set_names(list(.x), .y)))
  )
})

# ------------------------------------------------------------------------------
# size

furrr_test_that("future_pmap() works with completely empty list", {
  expect_identical(future_pmap(list(), identity), list())
  expect_identical(future_pmap_dbl(list(), identity), double())
})

furrr_test_that("future_pmap() works with size zero input", {
  expect_identical(future_pmap(list(list(), list()), identity), list())
})

furrr_test_that("atomic variants work with size zero input", {
  expect_identical(future_pmap_chr(list(list(), list()), identity), character())
  expect_identical(future_pmap_dbl(list(list(), list()), identity), double())
  expect_identical(future_pmap_int(list(list(), list()), identity), integer())
  expect_identical(future_pmap_lgl(list(list(), list()), identity), logical())
})

furrr_test_that("generic variant works with size zero input", {
  expect_identical(
    future_pmap_vec(list(list(), list()), identity),
    NULL
  )
  expect_identical(
    future_pmap_vec(list(list(), list()), identity, .ptype = integer()),
    integer()
  )
})

furrr_test_that("size one recycling works", {
  expect_identical(
    future_pmap(list(1, 1:2), ~ c(.x, .y)),
    list(c(1, 1), c(1, 2))
  )

  expect_identical(
    future_pmap(list(1:2, 1), ~ c(.x, .y)),
    list(c(1, 1), c(2, 1))
  )

  expect_identical(
    future_pmap(list(integer(), 1), ~ c(.x, .y)),
    list()
  )

  expect_identical(
    future_pmap(list(1, integer()), ~ c(.x, .y)),
    list()
  )
})

# TODO: Reenable this test after future issue is fixed
# https://github.com/futureverse/future/issues/820
# https://github.com/futureverse/furrr/issues/307
# furrr_test_that("generally can't recycle to size zero", {
#   expect_error(
#     future_pmap(list(1:2, integer()), ~ c(.x, .y)),
#     "Can't recycle"
#   )
#
#   expect_error(
#     future_pmap(list(integer(), 1:2), ~ c(.x, .y)),
#     "Can't recycle"
#   )
# })

# ------------------------------------------------------------------------------
# Miscellaneous

furrr_test_that("named arguments can be passed through", {
  vec_mean <- function(.x, .y, na.rm = FALSE) {
    mean(c(.x, .y), na.rm = na.rm)
  }

  x <- list(c(NA, 1), 1:2)

  expect_identical(
    future_pmap(x, vec_mean, na.rm = TRUE),
    list(1, 1.5)
  )
})

furrr_test_that("arguments can be matched by name", {
  x <- list(x = c(1, 2), y = c(3, 5))

  fn <- function(y, x) {
    y - x
  }

  expect_identical(future_pmap_dbl(x, fn), c(2, 3))
})

furrr_test_that("unused components can be absorbed", {
  x <- list(c(1, 2), c(3, 5))

  fn1 <- function(x) {
    x
  }
  fn2 <- function(x, ...) {
    x
  }

  # TODO: Reenable this test after future issue is fixed
  # https://github.com/futureverse/future/issues/820
  # https://github.com/futureverse/furrr/issues/307
  # expect_error(future_pmap_dbl(x, fn1))

  expect_identical(future_pmap_dbl(x, fn2), c(1, 2))
})

furrr_test_that("globals in `.x` and `.y` are found (#16)", {
  fn1 <- function(x) sum(x, na.rm = TRUE)
  fn2 <- function(x) sum(x, na.rm = FALSE)

  x <- list(c(1, 2, NA), c(2, 3, 4))

  fns1 <- map(x, ~ purrr::partial(fn1, x = .x))
  fns2 <- map(x, ~ purrr::partial(fn2, x = .x))

  expect_identical(
    future_pmap(list(fns1, fns2), ~ c(.x(), .y())),
    list(c(3, NA), c(9, 9))
  )
})

test_that("globals in `.l` are only exported to workers that use them", {
  plan(multisession, workers = 2)
  on.exit(plan(sequential), add = TRUE)

  # Use `local()` to ensure that the wrapper functions and the anonymous
  # functions created with `~` don't pick up extra globals
  my_wrapper1 <- local({
    my_mean1 <- function(x) mean(x, na.rm = TRUE)

    function(x) {
      my_mean1(x)
      exists("my_mean1")
    }
  })

  my_wrapper2 <- local({
    my_mean2 <- function(x) mean(x, na.rm = FALSE)

    function(x) {
      my_mean2(x)
      exists("my_mean1")
    }
  })

  x <- list(my_wrapper1, my_wrapper2)

  expect_identical(
    future_pmap_lgl(list(x), .f = ~ .x(c(1, NA))),
    c(TRUE, FALSE)
  )
})
