tests/testthat/test_await.R

context("await")

test_that("await captures success_state from status key", {
  f <- function(x) {
    if (x == 0) list(state = "success", y = 2) else list(state = "fail", y = NULL)
  }
  expect_silent(r <- await(f, x = 0, .status_key = "state",
                           .success_states = c("success", "fail")))

  expect_equal(r$state, "success")
  expect_equal(get_status(r), "success")
  expect_equal(r$y, 2)

  r <- await(f, x = 1, .status_key = "state", .success_states = c("success", "fail"))
  expect_equal(r$state, "fail")
  expect_equal(get_status(r), "fail")
  expect_null(r$y)
})

test_that("await calls f until terminal status", {
  fake_f <- mockery::mock(list(status = "running"),
                 list(status = "running"),
                 list(status = "succeeded"))
  await(fake_f, .status_key = "status", .interval = .001)
  mockery::expect_called(fake_f, 3)
})

test_that("await errors on timeout or if status_key not found", {
  msg <- "Timeout exceeded. Current status: running"
  f <- function(x) list(state = "running")
  expect_error(await(f, x = 1, .status_key = "state",
                     .success_states = c("this never occurs"),
                     .timeout = .001, .interval = .001), msg)

  f <- function(x) list(x)
  msg <- "Cannot find status"
  expect_error(await(f, x = 1), msg)
})

test_that("await throws a civis_await_error with failure state", {
  g <- function(x) {
    if (x == 0) {
      list(state = "success", y = 2)
    } else {
      list(state = "fail", y = NULL, error = "this failed on platform")
    }
  }

  msg <- "g\\(x = 1\\): this failed on platform"
  expect_error(await(g, x = 1, .success_states = "success", .error_states = "fail"), msg)

  e <- tryCatch(await(g, x = 1, .success_states = "success", .error_states = "fail"),
           "civis_await_error" = function(e) e)
  expect_is(e, c("civis_await_error", "civis_error", "error"))
  msg <- "g(x = 1): this failed on platform"
  expect_equal(e$message, msg)

  e2 <- tryCatch(await(g, x = 1, .success_states = "success", .error_states = "fail"),
                "civis_error" = function(e) e)
  expect_equal(e, e2)
})

test_that("await throws a civis_timeout_error on timeout", {
  f <- function(x) list(state = "running")

  msg <- "Timeout exceeded. Current status: running"
  expect_error(await(f, x = 0, .timeout = .001, .interval = .001), msg)

  e <- tryCatch(await(f, x = 0, .timeout = .001, .interval = .001),
                "civis_timeout_error" = function(e) e)
  expect_is(e, c("civis_await_error", "civis_error", "error"))
  msg <- "Timeout exceeded. Current status: running"
  expect_equal(e$message, msg)

  e2 <- tryCatch(await(f, x = 0, .timeout = .001, .interval = .001),
                "civis_error" = function(e) e)
  expect_equal(e, e2)
})

test_that("default interval is null", {
  expect_null(getOption("civis.default_polling_interval"))
})

test_that("get_error returns error data for civis_error", {
  f <- function(x) {
    if (x == 0) {
      list(state = "running", y = 2, error = NULL)
    } else {
      list(state = "fail", y = NULL, error = "this failed on platform")
    }
  }

  e <- tryCatch(await(f, x = 1, .success_states = "success", .error_states = "fail"),
                "civis_error" = function(e) e)
  e_data <- get_error(e)
  expect_equal(e_data$args, list(x = 1))
  expect_equal(e_data$f, "f")
  expect_equal(e_data$error, "this failed on platform")

  e <- tryCatch(await(f, x = 0, .timeout = .001, .error_states = "fail"),
                "civis_error" = function(e) e)
  e_data <- get_error(e)
  expect_equal(e_data$args, list(x = 0))
  expect_equal(e_data$f, "f")
  expect_null(e_data$error)
})

test_that("verbose produces correct messages", {
  f <- function(x) {
    x <- runif(1)
    if (x > .5) {
      return(list(state = "running", x = NULL))
    } else {
      return(list(state = "success", x = x))
    }
  }
  set.seed(4)
  msg <- capture_messages(await(f, x = 1, .status_key = "state",
                                .success_states = c("success", "fail"),
             .verbose = TRUE))
  patterns <- c("Status: running", "Retry 1 in 0.302 seconds")
  expect_true(all(sapply(patterns, grepl, x = msg)))

  set.seed(4)
  msg <- capture_messages(await(f, x = 1, .status_key = "state",
                                .success_states = c("success", "fail"),
                                .verbose = TRUE, .interval = .01))
  patterns <- c("Status: running", "Retry 1 in 0.01 seconds")
  expect_true(all(sapply(patterns, grepl, msg)))
})

test_that("call_once returns list with element called", {
  f <- function(id) return(list(state = "succeeded"))
  expect_true(call_once(f, 1, fname = "f")$called)

  f <- function(id) return(list(state = "partying instead"))
  expect_false(call_once(f, 1, fname = "f")$called)
})

test_that("if called, get_status(r$response) returns status", {
  f <- function(id) return(list(state = "succeeded"))
  expect_equal(get_status(call_once(f, 1, fname = "f")$response), "succeeded")
})

test_that("call_once captures completed_states and status_keys", {
  f <- function(id) return(list(party_status = "at the party"))
  expect_true(call_once(f, 1, .success_states = "at the party",
                        .status_key = "party_status",  fname = "f")$called)
})

test_that("safe_call_once catches civis_await_error", {
  f <- function(x) list(state = "succeeded", x = x)
  r <- safe_call_once(f, x = 1, fname = "f")
  expect_true(r$called)
  expect_equal(r$response$x, 1)
  expect_equal(get_status(r$response), "succeeded")

  f <- function(x) list(state = "failed", error = "platform error")
  e <- safe_call_once(f, x = 1, fname = "f")
  expect_is(e, c("civis_await_error", "civis_error", "error"))
})

f_rand <- function(job_id, run_id = 1, ...) {
  x <- runif(1)
  if (x < .9) {
    return(list(state = "succeeded", job_id = job_id, args = list(...)))
  } else {
    return(list(state = "partying instead", job_id = job_id, args = list(...)))
  }
}

test_that("await_all calls f until completion - univariate", {
  fake_f <- mockery::mock(list(status = "running"),
                          list(status = "running"),
                          list(status = "succeeded"), cycle = TRUE)
  await_all(fake_f, 1:2, .status_key = "status", .interval = .001)
  mockery::expect_called(fake_f, 6)
})

test_that("await_all returns list of completed responses - univariate", {
  set.seed(2)
  x <- await_all(f_rand, .x = 1:2)
  expect_is(x, "list")
  expect_equal(sapply(x, get_status), rep("succeeded", 2))
  expect_equal(lapply(x, function(x) attr(x, 'args')),
               list(list(job_id = 1), list(job_id = 2)))
})

test_that("await_all vectorizes over any argument - univariate", {
  x <- await_all(f_rand, .x = 1:2)
  expect_equal(sapply(x, function(x) x$job_id), 1:2)
})

test_that("await_all catches arbitrary status and keys - univariate", {
  f <- function(x) list(party_status = "going home", value = x)
  x <- await_all(f, .x = 1:2,  .status_key = "party_status",
                 .success_states = "going home", .verbose = TRUE)
  expect_equal(sapply(x, get_status), rep("going home", 2))
  expect_equal(sapply(x, function(x) x$value), 1:2)
})

test_that("await_all throws civis_timeout_error", {
  f <- function(x) list(state = "at the party")
  msg <- c("Timeout exceeded. Current status: at the party, at the party")
  expect_error(await_all(f, .x = 1:2, .timeout = .002, .interval = .001), msg)

  e <- tryCatch(await_all(f, .x = 1:2, .timeout = .002, .interval = .001),
           "civis_timeout_error" = function(e) e)

  get_error(e)

  e2 <- tryCatch(await_all(f, .x = 1:2, .timeout = .002, .interval = .001),
                "civis_error" = function(e) e)
  expect_equal(e, e2)
})

test_that("await_all catches mixed failure states", {
  f <- function(x) {
    switch(x, list(state = "succeeded"),
           list(state = "failed", error = "platform error"),
           list(state = "succeeded"))
  }
  r <- await_all(f, .x = 1:2)
  expect_true(is.civis_error(r[[2]]))
  expect_equal(get_error(r[[2]])$error, "platform error")
  expect_equal(get_error(r[[2]])$args, list(x = 2))
})

test_that("await_all verbose prints all tasks and status", {
  set.seed(2)
  msgs <- capture_messages(await_all(f_rand, .x = 1:5, .y = 1:5, .verbose = TRUE))
  expect_true(any(grepl("partying instead", x = msgs)))
  expect_equal(length(msgs), 5)
})

test_that("await_all throws an error if lengths of .x and .y differ", {
  expect_error(await_all(f, .x = 1:2, .y = 3:5))
})

test_that("await_all calls f until completion - multivariate", {
  fake_f <- mockery::mock(list(status = "running"),
                          list(status = "running"),
                          list(status = "succeeded"), cycle = TRUE)
  await_all(fake_f, .x = 1:2, .y = 3:4, .status_key = "status", .interval = .001)
  mockery::expect_called(fake_f, 6)
})

test_that("await_all returns list of completed responses - multivariate", {
  set.seed(2)
  x <- await_all(f_rand, .x = 1:2, .y = 3:4)
  expect_is(x, "list")
  expect_equal(sapply(x, get_status), rep("succeeded", 2))
  expect_equal(lapply(x, function(x) attr(x, 'args')),
               list(list(job_id = 1, run_id = 3),
                    list(job_id = 2, run_id = 4)))
})

test_that("await_all catches arbitrary status and keys - multivariate", {
  f <- function(x, y) list(party_status = "going home", value = x)
  x <- await_all(f, .x = 1:2, .y = 3:4, .status_key = "party_status",
                 .success_states = "going home", .verbose = TRUE)
  expect_equal(sapply(x, get_status), rep("going home", 2))
  expect_equal(sapply(x, function(x) x$value), 1:2)
})

Try the civis package in your browser

Any scripts or data that you put into this service are public.

civis documentation built on April 1, 2023, 12:01 a.m.