defmodule Gradient.ElixirCheckerTest do
  use ExUnit.Case
  doctest Gradient.ElixirChecker

  alias Gradient.ElixirChecker

  import Gradient.TestHelpers

  test "checker options" do
    ast = load("Elixir.SpecWrongName.beam")

    assert [] = ElixirChecker.check(ast, env([], ex_check: false))
    assert [] != ElixirChecker.check(ast, env([], ex_check: true))
  end

  test "all specs are correct" do
    ast = load("Elixir.CorrectSpec.beam")

    assert [] = ElixirChecker.check(ast, env())
  end

  test "specs over default args are correct" do
    ast = load("Elixir.SpecDefaultArgs.beam")

    assert [] = ElixirChecker.check(ast, env())
  end

  test "spec arity doesn't match the function arity" do
    ast = load("Elixir.SpecWrongArgsArity.beam")

    assert [{_, {:spec_error, :wrong_spec_name, 2, :foo, 3}}] = ElixirChecker.check(ast, env())
  end

  test "spec name doesn't match the function name" do
    ast = load("Elixir.SpecWrongName.beam")

    assert [
             {_, {:spec_error, :wrong_spec_name, 5, :convert, 1}},
             {_, {:spec_error, :wrong_spec_name, 11, :last_two, 1}}
           ] = ElixirChecker.check(ast, env())
  end

  test "spec defined in a __using__ macro with tokens" do
    {tokens, ast} = load("Elixir.SpecInMacro.beam", "spec_in_macro.ex")

    assert [] = ElixirChecker.check(ast, env(tokens))
  end

  test "spec defined in a __using__ macro without tokens" do
    ast = load("Elixir.SpecInMacro.beam")

    assert [] = ElixirChecker.check(ast, env())
  end

  test "--warn-missing-spec(-all) option" do
    ast = load("Elixir.SpecsNoSpecs.beam")

    assert [] = ElixirChecker.check(ast, env())

    assert [
             {_, {:spec_error, :no_spec, 16, :g1, 0}}
           ] = ElixirChecker.check(ast, env([], [{:warn_missing_spec, :exported}]))

    assert [
             {_, {:spec_error, :no_spec, 7, :g, 0}},
             {_, {:spec_error, :no_spec, 16, :g1, 0}}
           ] = ElixirChecker.check(ast, env([], [{:warn_missing_spec, :all}]))
  end

  defp env(tokens \\ [], opts \\ []) do
    [{:env, Gradient.build_env(tokens)} | opts]
  end
end
