summaryrefslogtreecommitdiff
path: root/lib/mix/test/mix/tasks/compile.erlang_test.exs
blob: 3967570c0cdef2403c3c7920eb34b23e4640a05f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
Code.require_file("../../test_helper.exs", __DIR__)

defmodule Mix.Tasks.Compile.ErlangTest do
  use MixTest.Case
  import ExUnit.CaptureIO

  defmacro position(line, column), do: {line, column}

  setup config do
    erlc_options = Map.get(config, :erlc_options, [])
    Mix.ProjectStack.post_config(erlc_options: erlc_options)
    Mix.Project.push(MixTest.Case.Sample)
    :ok
  end

  @tag erlc_options: [{:d, ~c"foo", ~c"bar"}]
  test "raises on invalid erlc_options" do
    in_fixture("compile_erlang", fn ->
      assert_raise Mix.Error, ~r"Compiling Erlang file '.*' failed", fn ->
        capture_io(fn ->
          Mix.Tasks.Compile.Erlang.run([])
        end)
      end
    end)
  end

  test "compiles and cleans src/b.erl and src/c.erl" do
    in_fixture("compile_erlang", fn ->
      assert Mix.Tasks.Compile.Erlang.run(["--verbose"]) == {:ok, []}
      assert_received {:mix_shell, :info, ["Compiled src/b.erl"]}
      assert_received {:mix_shell, :info, ["Compiled src/c.erl"]}

      assert File.regular?("_build/dev/lib/sample/ebin/b.beam")
      assert File.regular?("_build/dev/lib/sample/ebin/c.beam")

      assert Mix.Tasks.Compile.Erlang.run(["--verbose"]) == {:noop, []}
      refute_received {:mix_shell, :info, ["Compiled src/b.erl"]}

      assert Mix.Tasks.Compile.Erlang.run(["--force", "--verbose"]) == {:ok, []}
      assert_received {:mix_shell, :info, ["Compiled src/b.erl"]}
      assert_received {:mix_shell, :info, ["Compiled src/c.erl"]}

      assert Mix.Tasks.Compile.Erlang.clean()
      refute File.regular?("_build/dev/lib/sample/ebin/b.beam")
      refute File.regular?("_build/dev/lib/sample/ebin/c.beam")
    end)
  end

  test "removes old artifact files" do
    in_fixture("compile_erlang", fn ->
      assert Mix.Tasks.Compile.Erlang.run([]) == {:ok, []}
      assert File.regular?("_build/dev/lib/sample/ebin/b.beam")

      File.rm!("src/b.erl")
      assert Mix.Tasks.Compile.Erlang.run([]) == {:ok, []}
      refute File.regular?("_build/dev/lib/sample/ebin/b.beam")
    end)
  end

  test "compilation purges the module" do
    in_fixture("compile_erlang", fn ->
      # Create the first version of the module.
      defmodule :purge_test do
        def version, do: :v1
      end

      assert :v1 == :purge_test.version()

      # Create the second version of the module (this time as Erlang source).
      File.write!("src/purge_test.erl", """
      -module(purge_test).
      -export([version/0]).
      version() -> v2.
      """)

      assert Mix.Tasks.Compile.Erlang.run([]) == {:ok, []}

      # If the module was not purged on recompilation, this would fail.
      assert :v2 == :purge_test.version()
    end)
  end

  test "continues even if one file fails to compile" do
    in_fixture("compile_erlang", fn ->
      file = Path.absname("src/zzz.erl")

      File.write!(file, """
      -module(zzz).
      def zzz(), do: b
      """)

      capture_io(fn ->
        assert {:error, [diagnostic]} = Mix.Tasks.Compile.Erlang.run([])

        assert %Mix.Task.Compiler.Diagnostic{
                 compiler_name: "erl_parse",
                 file: ^file,
                 message: "syntax error before: zzz",
                 position: position(2, 5),
                 severity: :error
               } = diagnostic
      end)

      assert File.regular?("_build/dev/lib/sample/ebin/b.beam")
      assert File.regular?("_build/dev/lib/sample/ebin/c.beam")
    end)
  end

  test "saves warnings between builds" do
    in_fixture("compile_erlang", fn ->
      file = Path.absname("src/has_warning.erl")

      File.write!(file, """
      -module(has_warning).
      my_fn() -> ok.
      """)

      capture_io(fn ->
        assert {:ok, [diagnostic]} = Mix.Tasks.Compile.Erlang.run([])

        assert %Mix.Task.Compiler.Diagnostic{
                 file: ^file,
                 compiler_name: "erl_lint",
                 message: "function my_fn/0 is unused",
                 position: position(2, 1),
                 severity: :warning
               } = diagnostic

        # Should return warning without recompiling file
        assert {:noop, [^diagnostic]} = Mix.Tasks.Compile.Erlang.run(["--verbose"])
        refute_received {:mix_shell, :info, ["Compiled src/has_warning.erl"]}

        # Should not return warning after changing file
        File.write!(file, """
        -module(has_warning).
        -export([my_fn/0]).
        my_fn() -> ok.
        """)

        ensure_touched(file)
        assert {:ok, []} = Mix.Tasks.Compile.Erlang.run([])
      end)
    end)
  end

  test "prints warnings from stale files with --all-warnings" do
    in_fixture("compile_erlang", fn ->
      file = Path.absname("src/has_warning.erl")

      File.write!(file, """
      -module(has_warning).
      my_fn() -> ok.
      """)

      capture_io(fn -> Mix.Tasks.Compile.Erlang.run([]) end)

      output =
        capture_io(fn ->
          assert {:noop, _} = Mix.Tasks.Compile.Erlang.run(["--all-warnings"])
        end)

      assert output =~ ~r"src/has_warning.erl:2:(1:)? warning: function my_fn/0 is unused\n"

      # Should not print old warnings after fixing
      File.write!(file, """
      -module(has_warning).
      """)

      ensure_touched(file)

      output =
        capture_io(fn ->
          Mix.Tasks.Compile.Erlang.run(["--all-warnings"])
        end)

      assert output == ""
    end)
  end

  @tag erlc_options: [{:warnings_as_errors, true}]
  test "adds :debug_info to erlc_options by default" do
    in_fixture("compile_erlang", fn ->
      Mix.Tasks.Compile.Erlang.run([])

      binary = File.read!("_build/dev/lib/sample/ebin/b.beam")

      {:ok, {_, [debug_info: {:debug_info_v1, _, {debug_info, _}}]}} =
        :beam_lib.chunks(binary, [:debug_info])

      assert debug_info != :none
    end)
  end
end