test_that("returns condition or value", {
  expect_equal(expect_error(1, NA), 1)
  expect_s3_class(expect_error(stop("!")), "simpleError")
})

test_that("regexp = NULL checks for presence of error", {
  expect_success(expect_error(stop()))
  expect_snapshot_failure(expect_error(null()))
})

test_that("regexp = NA checks for absence of error", {
  expect_success(expect_error(null(), NA))
  expect_failure(expect_error(stop("Yes"), NA))
})

test_that("regexp = string matches for error message", {
  expect_success(expect_error(stop("Yes"), "Yes"))
  expect_error(expect_error(stop("Yes"), "No"))
  expect_snapshot_failure(expect_error("OK", "No"))
})

test_that("class = string matches class of error", {
  blah <- function() {
    abort("hi", class = c("blah", "error", "condition"))
  }

  expect_success(expect_error(blah(), class = "blah"))
  expect_error(expect_error(blah(), class = "blech"), class = "blah")
})

test_that("base_class must match when class is set", {
  foo <- function() warn("foo", class = "bar")
  expect_warning(expect_failure(expect_error(foo(), class = "bar")))
  expect_success(expect_warning(foo(), class = "bar"))
})

test_that("check type of class and pattern", {
  expect_snapshot(error = TRUE, {
    expect_error(stop("!"), regexp = 1)
    expect_error(stop("!"), class = 1)
  })
})

test_that("... passed on to grepl", {
  expect_success(expect_error(stop("X"), "x", ignore.case = TRUE))
})

test_that("message method is called when expecting error", {
  local_bindings(
    conditionMessage.foobar = function(err) "dispatched!",
    .env = globalenv()
  )
  fb <- function() abort("foobar", "foobar")

  expect_error(fb(), "dispatched!", class = "foobar")
  expect_snapshot_failure(expect_error(fb(), NA))
})

test_that("rlang backtrace reminders are not included in error message", {
  f <- function() g()
  g <- function() h()
  h <- function() abort("foo")
  expect_error(f(), "foo$")
})

test_that("can capture Throwable conditions from rJava", {
  local_bindings(
    conditionMessage.Throwable = function(c, ...) unclass(c)$message,
    conditionCall.Throwable = function(c, ...) unclass(c)$call,
    `$.Throwable` = function(...) stop("forbidden"),
    `$<-.Throwable` = function(...) stop("forbidden"),
    .env = globalenv()
  )

  throw <- function(msg) stop(error_cnd("Throwable", message = msg))
  expect_error(throw("foo"), "foo", class = "Throwable")
})

# expect_warning() ----------------------------------------------------------

test_that("warnings are converted to errors when options('warn') >= 2", {
  withr::with_options(c(warn = 2), {
    expect_warning(warning("foo"))
    expect_error(warning("foo"))
  })
})

test_that("can silence warnings", {
  expect_warning(suppressWarnings(warning("foo")), NA)

  # Can't test with `expect_warning()` because the warning is still
  # signalled, it's just not printed
  # https://github.com/wch/r-source/blob/886ab4a0/src/main/errors.c#L388-L484
  withr::with_options(c(warn = -1), warning("foo"))
})

test_that("when checking for no warnings, exclude deprecation warnings", {
  foo <- function() {
    lifecycle::deprecate_warn("1.0.0", "foo()")
  }
  expect_warning(
    expect_warning(foo(), NA),
    class = "lifecycle_warning_deprecated"
  )
})

test_that("when checking for no warnings, exclude deprecation warnings (2e)", {
  local_edition(2)

  foo <- function() {
    options(lifecycle_verbosity = "warning")
    lifecycle::deprecate_warn("1.0.0", "foo()")
  }

  expect_warning(
    expect_warning(foo(), NA),
    class = "lifecycle_warning_deprecated"
  )
})

# expect_message ----------------------------------------------------------

test_that("regexp = NA checks for absence of message", {
  expect_success(expect_message(null(), NA))
  expect_failure(expect_message(message("!"), NA))
})

# expect_condition --------------------------------------------------------

test_that("continues evaluation", {
  expect_condition({
    message("Hi")
    new_variable <- 1
  })
  expect_equal(exists("new_variable"), TRUE)
})

test_that("but not after error", {
  expect_condition({
    stop("Hi")
    new_variable <- 1
  })
  expect_equal(exists("new_variable"), FALSE)
})

test_that("captured condition is muffled", {
  expect_message(expect_condition(message("Hi")), NA)
  expect_warning(expect_condition(warning("Hi")), NA)
  expect_error(expect_condition(stop("Hi")), NA)
})

test_that("only matching condition is captured, others bubble up", {
  f1 <- function() {
    message("Hi")
    message("Bye")
  }
  expect_condition(expect_condition(f1(), "Hi"), "Bye")
  expect_condition(expect_condition(f1(), "Bye"), "Hi")

  f2 <- function() {
    message("Hi")
    stop("Bye")
  }
  expect_error(expect_condition(f2(), "Hi"), "Bye")
})

test_that("cnd expectations consistently return condition (#1371)", {
  f <- function(out, action) {
    action
    out
  }

  expect_s3_class(expect_message(f(NULL, message(""))), "simpleMessage")
  expect_s3_class(expect_warning(f(NULL, warning(""))), "simpleWarning")
  expect_s3_class(expect_error(f(NULL, stop(""))), "simpleError")

  # Used to behave differently with non-`NULL` values
  expect_s3_class(expect_message(f("return value", message(""))), "simpleMessage")
  expect_s3_class(expect_warning(f("return value", warning(""))), "simpleWarning")
  expect_s3_class(expect_error(f("return value", stop(""))), "simpleError")

  # If there is no condition expected we return the value
  expect_equal(expect_message(f("return value", NULL), regexp = NA), "return value")
  expect_equal(expect_warning(f("return value", NULL), regexp = NA), "return value")
  expect_equal(expect_error(f("return value", NULL), regexp = NA), "return value")
})

test_that("cli width wrapping doesn't affect text matching", {
  skip_if_not_installed("cli", "3.0.2")
  skip_if_not_installed("rlang", "1.0.0")

  local_use_cli()

  expect_error(
    abort("foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz"),
    "foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz foobarbaz"
  )
})

test_that("can match parent conditions (#1493)", {
  parent <- error_cnd("foo", message = "Parent message.")
  f <- function() abort("Tilt.", parent = parent)

  expect_error(f(), class = "foo")
  expect_error(f(), "^Parent message.$")

  # Pattern and class must match the same condition
  expect_error(expect_error(f(), "Tilt.", class = "foo"))

  # Can disable parent matching
  expect_error(expect_error(f(), class = "foo", inherit = FALSE))
  expect_error(expect_error(f(), "Parent message.", inherit = FALSE))
})

test_that("unused arguments generate a warning", {
  expect_snapshot(expect_condition(stop("Hi!"), foo = "bar"))
})


# second edition ----------------------------------------------------------

test_that("other conditions are swallowed", {
  f <- function(...) {
    conds <- c(...)
    for (cond in conds) {
      switch(cond,
        message = message("message"),
        warning = warning("warning"),
        error = stop("error"),
        condition = signal("signal", class = "signal")
      )
    }
  }

  local_edition(2)
  # if condition text doesn't match, expectation fails (not errors)
  expect_failure(expect_error(f("error"), "not a match"))
  expect_failure(expect_warning(f("warning"), "not a match"))
  expect_failure(expect_message(f("message"), "not a match"))
  expect_failure(expect_condition(f("condition"), "not a match"))

  # if error/condition class doesn't match, expectation fails
  expect_failure(expect_error(f("error"), class = "not a match"))
  expect_failure(expect_condition(f("message"), class = "not a match"))

  # expect_message() and expect_warning() swallow all messages/warnings
  expect_message(expect_message(f("message", "message")), NA)
  expect_warning(expect_warning(f("warning", "warning")), NA)
})

test_that("can match parent conditions (edition 2, #1493)", {
  local_edition(2)

  parent <- error_cnd("foo", message = "Parent message.")
  f <- function() abort("Tilt.", parent = parent)

  expect_error(f(), class = "foo")
  expect_error(f(), "^Parent message.$")

  # Can disable parent matching
  expect_error(expect_error(f(), class = "foo", inherit = FALSE))
  expect_error(expect_error(f(), "Parent message.", inherit = FALSE))
})
