test_that("assignment_linter skips allowed usages", {
  linter <- assignment_linter()

  expect_lint("blah", NULL, linter)
  expect_lint("blah <- 1", NULL, linter)
  expect_lint("blah<-1", NULL, linter)
  expect_lint("fun(blah=1)", NULL, linter)
})

test_that("assignment_linter blocks disallowed usages", {
  linter <- assignment_linter()
  lint_msg <- rex::rex("Use <-, not =, for assignment.")

  expect_lint("blah=1", lint_msg, linter)
  expect_lint("blah = 1", lint_msg, linter)
  expect_lint("blah = fun(1)", lint_msg, linter)
  expect_lint("fun((blah = fun(1)))", lint_msg, linter)

  expect_lint(
    "blah = fun(1) {",
    list(
      lint_msg,
      c(type = "error", "unexpected")
    ),
    linter
  )
})

test_that("arguments handle <<- and ->/->> correctly", {
  expect_lint("1 -> blah", rex::rex("Use <-, not ->, for assignment."), assignment_linter())
  expect_lint("1 ->> blah", rex::rex("->> can have hard-to-predict behavior;"), assignment_linter())

  # <<- is only blocked optionally
  expect_lint("1 <<- blah", NULL, assignment_linter())
  expect_lint(
    "1 <<- blah",
    rex::rex("<<- can have hard-to-predict behavior;"),
    assignment_linter(allow_cascading_assign = FALSE)
  )

  # blocking -> can be disabled
  expect_lint("1 -> blah", NULL, assignment_linter(allow_right_assign = TRUE))
  expect_lint("1 ->> blah", NULL, assignment_linter(allow_right_assign = TRUE))
  # blocked under cascading assign but not under right assign --> blocked
  expect_lint(
    "1 ->> blah",
    rex::rex("->> can have hard-to-predict behavior;"),
    assignment_linter(allow_cascading_assign = FALSE, allow_right_assign = TRUE)
  )
})

test_that("arguments handle trailing assignment operators correctly", {
  expect_lint("x <- y", NULL, assignment_linter(allow_trailing = FALSE))
  expect_lint("foo(bar = 1)", NULL, assignment_linter(allow_trailing = FALSE))

  expect_lint(
    "foo(bar =\n1)",
    rex::rex("= should not be trailing at the end of a line."),
    assignment_linter(allow_trailing = FALSE)
  )

  expect_lint(
    "x <<-\ny",
    rex::rex("<<- should not be trailing"),
    assignment_linter(allow_trailing = FALSE)
  )
  expect_lint(
    "x <<-\ny",
    rex::rex("<<- can have hard-to-predict behavior"),
    assignment_linter(allow_trailing = FALSE, allow_cascading_assign = FALSE)
  )

  expect_lint(
    "x <- #Test \ny",
    rex::rex("<- should not be trailing"),
    assignment_linter(allow_trailing = FALSE)
  )

  expect_lint(
    "is_long <-\nis %>%\ngather(measure, value, -Species) %>%\narrange(-value)",
    NULL,
    assignment_linter()
  )
  expect_lint(
    "is_long <-\nis %>%\ngather(measure, value, -Species) %>%\narrange(-value)",
    rex::rex("<- should not be trailing"),
    assignment_linter(allow_trailing = FALSE)
  )

  expect_lint(
    "is %>%\ngather(measure, value, -Species) %>%\narrange(-value) ->\nis_long",
    rex::rex("Use <-, not ->"),
    assignment_linter()
  )
  expect_lint(
    "is %>%\ngather(measure, value, -Species) %>%\narrange(-value) ->\nis_long",
    rex::rex("Use <-, not ->"),
    assignment_linter(allow_trailing = FALSE)
  )
  expect_lint(
    "is %>%\ngather(measure, value, -Species) %>%\narrange(-value) ->\nis_long",
    rex::rex("-> should not be trailing"),
    assignment_linter(allow_right_assign = TRUE, allow_trailing = FALSE)
  )

  expect_lint(
    "\n\nblah=\n42\nblh2<-\n54",
    list(
      list(message = "=", line_number = 3L, column_number = 5L),
      list(message = "<-", line_number = 5L, column_number = 5L)
    ),
    assignment_linter(allow_trailing = FALSE)
  )
})

test_that("allow_trailing interacts correctly with comments in braced expressions", {
  linter <- assignment_linter(allow_trailing = FALSE)
  expect_lint(
    trim_some("
    {
      x <- 1
      # blah
      y <- 2
    }
    "),
    NULL,
    linter
  )

  expect_lint(
    trim_some("
    {
      x <- '#x'
      y <- '#y'
    }
    "),
    NULL,
    linter
  )

  expect_lint(
    trim_some("
    {
      x <- # blah
        'x'
    }
    "),
    list(message = "<-", line_number = 2L),
    linter
  )

  expect_lint(
    trim_some("
    {
      x <- '
        a string
        with an assignment <-
        at the end of the line
      '
    }
    "),
    NULL,
    linter
  )
})

test_that("%<>% throws a lint", {
  expect_lint("x %<>% sum()", "Avoid the assignment pipe %<>%", assignment_linter())
  expect_lint("x %<>% sum()", NULL, assignment_linter(allow_pipe_assign = TRUE))

  # interaction with allow_trailing
  expect_lint("x %<>%\n  sum()", "Assignment %<>% should not be trailing", assignment_linter(allow_trailing = FALSE))
})

test_that("multiple lints throw correct messages", {
  expect_lint(
    "{ x <<- 1; y ->> 2; z -> 3; x %<>% as.character() }",
    list(
      list(message = "<<- can have hard-to-predict behavior"),
      list(message = "->> can have hard-to-predict behavior"),
      list(message = "Use <-, not ->"),
      list(message = "Avoid the assignment pipe %<>%")
    ),
    assignment_linter(allow_cascading_assign = FALSE)
  )
})
