1Code.require_file("../../test_helper.exs", __DIR__) 2 3defmodule Mix.Tasks.Compile.ErlangTest do 4 use MixTest.Case 5 import ExUnit.CaptureIO 6 7 if System.otp_release() >= "24" do 8 defmacro position(line, column), do: {line, column} 9 else 10 defmacro position(line, _column), do: line 11 end 12 13 setup config do 14 erlc_options = Map.get(config, :erlc_options, []) 15 Mix.ProjectStack.post_config(erlc_options: erlc_options) 16 Mix.Project.push(MixTest.Case.Sample) 17 :ok 18 end 19 20 @tag erlc_options: [{:d, 'foo', 'bar'}] 21 test "raises on invalid erlc_options" do 22 in_fixture("compile_erlang", fn -> 23 assert_raise Mix.Error, ~r"Compiling Erlang file '.*' failed", fn -> 24 capture_io(fn -> 25 Mix.Tasks.Compile.Erlang.run([]) 26 end) 27 end 28 end) 29 end 30 31 test "compiles and cleans src/b.erl and src/c.erl" do 32 in_fixture("compile_erlang", fn -> 33 assert Mix.Tasks.Compile.Erlang.run(["--verbose"]) == {:ok, []} 34 assert_received {:mix_shell, :info, ["Compiled src/b.erl"]} 35 assert_received {:mix_shell, :info, ["Compiled src/c.erl"]} 36 37 assert File.regular?("_build/dev/lib/sample/ebin/b.beam") 38 assert File.regular?("_build/dev/lib/sample/ebin/c.beam") 39 40 assert Mix.Tasks.Compile.Erlang.run(["--verbose"]) == {:noop, []} 41 refute_received {:mix_shell, :info, ["Compiled src/b.erl"]} 42 43 assert Mix.Tasks.Compile.Erlang.run(["--force", "--verbose"]) == {:ok, []} 44 assert_received {:mix_shell, :info, ["Compiled src/b.erl"]} 45 assert_received {:mix_shell, :info, ["Compiled src/c.erl"]} 46 47 assert Mix.Tasks.Compile.Erlang.clean() 48 refute File.regular?("_build/dev/lib/sample/ebin/b.beam") 49 refute File.regular?("_build/dev/lib/sample/ebin/c.beam") 50 end) 51 end 52 53 test "removes old artifact files" do 54 in_fixture("compile_erlang", fn -> 55 assert Mix.Tasks.Compile.Erlang.run([]) == {:ok, []} 56 assert File.regular?("_build/dev/lib/sample/ebin/b.beam") 57 58 File.rm!("src/b.erl") 59 assert Mix.Tasks.Compile.Erlang.run([]) == {:ok, []} 60 refute File.regular?("_build/dev/lib/sample/ebin/b.beam") 61 end) 62 end 63 64 test "compilation purges the module" do 65 in_fixture("compile_erlang", fn -> 66 # Create the first version of the module. 67 defmodule :purge_test do 68 def version, do: :v1 69 end 70 71 assert :v1 == :purge_test.version() 72 73 # Create the second version of the module (this time as Erlang source). 74 File.write!("src/purge_test.erl", """ 75 -module(purge_test). 76 -export([version/0]). 77 version() -> v2. 78 """) 79 80 assert Mix.Tasks.Compile.Erlang.run([]) == {:ok, []} 81 82 # If the module was not purged on recompilation, this would fail. 83 assert :v2 == :purge_test.version() 84 end) 85 end 86 87 test "continues even if one file fails to compile" do 88 in_fixture("compile_erlang", fn -> 89 file = Path.absname("src/zzz.erl") 90 91 File.write!(file, """ 92 -module(zzz). 93 def zzz(), do: b 94 """) 95 96 capture_io(fn -> 97 assert {:error, [diagnostic]} = Mix.Tasks.Compile.Erlang.run([]) 98 99 assert %Mix.Task.Compiler.Diagnostic{ 100 compiler_name: "erl_parse", 101 file: ^file, 102 message: "syntax error before: zzz", 103 position: position(2, 5), 104 severity: :error 105 } = diagnostic 106 end) 107 108 assert File.regular?("_build/dev/lib/sample/ebin/b.beam") 109 assert File.regular?("_build/dev/lib/sample/ebin/c.beam") 110 end) 111 end 112 113 test "saves warnings between builds" do 114 in_fixture("compile_erlang", fn -> 115 file = Path.absname("src/has_warning.erl") 116 117 File.write!(file, """ 118 -module(has_warning). 119 my_fn() -> ok. 120 """) 121 122 capture_io(fn -> 123 assert {:ok, [diagnostic]} = Mix.Tasks.Compile.Erlang.run([]) 124 125 assert %Mix.Task.Compiler.Diagnostic{ 126 file: ^file, 127 compiler_name: "erl_lint", 128 message: "function my_fn/0 is unused", 129 position: position(2, 1), 130 severity: :warning 131 } = diagnostic 132 133 # Should return warning without recompiling file 134 assert {:noop, [^diagnostic]} = Mix.Tasks.Compile.Erlang.run(["--verbose"]) 135 refute_received {:mix_shell, :info, ["Compiled src/has_warning.erl"]} 136 137 # Should not return warning after changing file 138 File.write!(file, """ 139 -module(has_warning). 140 -export([my_fn/0]). 141 my_fn() -> ok. 142 """) 143 144 ensure_touched(file) 145 assert {:ok, []} = Mix.Tasks.Compile.Erlang.run([]) 146 end) 147 end) 148 end 149 150 test "prints warnings from stale files with --all-warnings" do 151 in_fixture("compile_erlang", fn -> 152 file = Path.absname("src/has_warning.erl") 153 154 File.write!(file, """ 155 -module(has_warning). 156 my_fn() -> ok. 157 """) 158 159 capture_io(fn -> Mix.Tasks.Compile.Erlang.run([]) end) 160 161 output = 162 capture_io(fn -> 163 assert {:noop, _} = Mix.Tasks.Compile.Erlang.run(["--all-warnings"]) 164 end) 165 166 assert output =~ ~r"src/has_warning.erl:2:(1:)? warning: function my_fn/0 is unused\n" 167 168 # Should not print old warnings after fixing 169 File.write!(file, """ 170 -module(has_warning). 171 """) 172 173 ensure_touched(file) 174 175 output = 176 capture_io(fn -> 177 Mix.Tasks.Compile.Erlang.run(["--all-warnings"]) 178 end) 179 180 assert output == "" 181 end) 182 end 183 184 @tag erlc_options: [{:warnings_as_errors, true}] 185 test "adds :debug_info to erlc_options by default" do 186 in_fixture("compile_erlang", fn -> 187 Mix.Tasks.Compile.Erlang.run([]) 188 189 binary = File.read!("_build/dev/lib/sample/ebin/b.beam") 190 191 {:ok, {_, [debug_info: {:debug_info_v1, _, {debug_info, _}}]}} = 192 :beam_lib.chunks(binary, [:debug_info]) 193 194 assert debug_info != :none 195 end) 196 end 197end 198