1Code.require_file("../../test_helper.exs", __DIR__) 2 3defmodule Mix.Tasks.EscriptTest do 4 use MixTest.Case 5 6 defmodule Escript do 7 def project do 8 [ 9 app: :escript_test, 10 version: "0.0.1", 11 escript: [main_module: EscriptTest, name: "escript_test", embed_elixir: true] 12 ] 13 end 14 end 15 16 defmodule EscriptWithDebugInfo do 17 def project do 18 [ 19 app: :escript_test_with_debug_info, 20 version: "0.0.1", 21 escript: [main_module: EscriptTest, strip_beams: false] 22 ] 23 end 24 end 25 26 defmodule EscriptWithPath do 27 def project do 28 [ 29 app: :escript_test_with_path, 30 version: "0.0.1", 31 escript: [ 32 app: nil, 33 embed_elixir: true, 34 main_module: EscriptTest, 35 path: Path.join("ebin", "escript_test_with_path") 36 ] 37 ] 38 end 39 end 40 41 defmodule EscriptWithDeps do 42 def project do 43 [ 44 app: :escript_test_with_deps, 45 version: "0.0.1", 46 escript: [main_module: EscriptTest], 47 deps: [{:ok, path: fixture_path("deps_status/deps/ok")}] 48 ] 49 end 50 end 51 52 defmodule EscriptErlangWithDeps do 53 def project do 54 [ 55 app: :escript_test_erlang_with_deps, 56 version: "0.0.1", 57 language: :erlang, 58 escript: [main_module: :escript_test], 59 deps: [{:ok, path: fixture_path("deps_status/deps/ok")}] 60 ] 61 end 62 63 def application do 64 [applications: [], extra_applications: [:crypto, elixir: :optional]] 65 end 66 end 67 68 defmodule EscriptErlangMainModule do 69 def project do 70 [ 71 app: :escript_test_erlang_main_module, 72 version: "0.0.1", 73 escript: [main_module: :escript_test] 74 ] 75 end 76 end 77 78 defmodule EscriptWithUnknownMainModule do 79 def project do 80 [ 81 app: :escript_test_with_unknown_main_module, 82 version: "0.0.1", 83 escript: [main_module: BogusEscriptTest] 84 ] 85 end 86 end 87 88 defmodule EscriptConsolidated do 89 def project do 90 [ 91 app: :escript_test_consolidated, 92 build_embedded: true, 93 version: "0.0.1", 94 escript: [main_module: EscriptTest] 95 ] 96 end 97 end 98 99 test "generate escript" do 100 in_fixture("escript_test", fn -> 101 Mix.Project.push(Escript) 102 103 Mix.Tasks.Escript.Build.run([]) 104 assert_received {:mix_shell, :info, ["Generated escript escript_test with MIX_ENV=dev"]} 105 assert System.cmd("escript", ["escript_test"]) == {"TEST\n", 0} 106 assert count_abstract_code("escript_test") == 0 107 end) 108 end 109 110 test "generate escript with --no-compile option" do 111 in_fixture("escript_test", fn -> 112 Mix.Project.push(Escript) 113 114 Mix.Tasks.Compile.run([]) 115 purge([EscriptTest]) 116 117 Mix.Tasks.Escript.Build.run(["--no-compile"]) 118 assert_received {:mix_shell, :info, ["Generated escript escript_test with MIX_ENV=dev"]} 119 end) 120 end 121 122 test "generate escript with compile config" do 123 in_fixture("escript_test", fn -> 124 Mix.Project.push(Escript) 125 126 File.mkdir_p!("config") 127 128 File.write!("config/config.exs", ~S""" 129 import Config 130 config :foobar, :value, "COMPILE #{config_env()} TARGET #{config_target()}" 131 """) 132 133 Mix.Tasks.Escript.Build.run([]) 134 assert_received {:mix_shell, :info, ["Generated escript escript_test with MIX_ENV=dev"]} 135 assert System.cmd("escript", ["escript_test"]) == {"COMPILE dev TARGET host\n", 0} 136 end) 137 end 138 139 test "generate escript with runtime config" do 140 in_fixture("escript_test", fn -> 141 Mix.Project.push(Escript) 142 143 File.mkdir_p!("config") 144 145 File.write!("config/config.exs", """ 146 [foobar: [value: "OLD", nesting: [config: true, runtime: false]]] 147 """) 148 149 File.write!("config/runtime.exs", ~S""" 150 import Config 151 config :foobar, :value, "RUNTIME #{config_env()} TARGET #{config_target()} ARGV #{System.argv()}" 152 config :foobar, :nesting, runtime: true 153 """) 154 155 Mix.Tasks.Escript.Build.run([]) 156 assert_received {:mix_shell, :info, ["Generated escript escript_test with MIX_ENV=dev"]} 157 158 assert System.cmd("escript", ["escript_test", "--foo", "--bar"]) == 159 {"RUNTIME dev TARGET host ARGV --foo--bar\n", 0} 160 161 assert System.cmd("escript", ["escript_test", "--nesting"]) == 162 {"[config: true, runtime: true]\n", 0} 163 end) 164 end 165 166 test "generate escript with debug information" do 167 in_fixture("escript_test", fn -> 168 Mix.Project.push(EscriptWithDebugInfo) 169 170 Mix.Tasks.Escript.Build.run([]) 171 172 msg = "Generated escript escript_test_with_debug_info with MIX_ENV=dev" 173 assert_received {:mix_shell, :info, [^msg]} 174 175 assert System.cmd("escript", ["escript_test_with_debug_info"]) == {"TEST\n", 0} 176 assert count_abstract_code("escript_test_with_debug_info") > 0 177 end) 178 end 179 180 defp count_abstract_code(escript_filename) do 181 escript_filename 182 |> read_beams() 183 |> Enum.count(fn {_, beam} -> get_abstract_code(beam) end) 184 end 185 186 defp read_beams(escript_filename) do 187 # :zip.unzip/2 cannot unzip an escript unless we remove the escript header 188 zip_data = remove_escript_header(File.read!(escript_filename)) 189 {:ok, tuples} = :zip.unzip(zip_data, [:memory]) 190 191 for {filename, beam} <- tuples, Path.extname(filename) == ".beam" do 192 {filename, beam} 193 end 194 end 195 196 defp remove_escript_header(escript_data) do 197 {offset, _length} = :binary.match(escript_data, "\nPK") 198 zip_start = offset + 1 199 binary_part(escript_data, zip_start, byte_size(escript_data) - zip_start) 200 end 201 202 defp get_abstract_code(beam) do 203 case :beam_lib.chunks(beam, [:abstract_code]) do 204 {:ok, {_, [{:abstract_code, {_, abstract_code}}]}} -> abstract_code 205 _ -> nil 206 end 207 end 208 209 test "generate escript with path" do 210 in_fixture("escript_test", fn -> 211 Mix.Project.push(EscriptWithPath) 212 213 Mix.Tasks.Escript.Build.run([]) 214 215 message = "Generated escript ebin/escript_test_with_path with MIX_ENV=dev" 216 assert_received {:mix_shell, :info, [^message]} 217 218 assert System.cmd("escript", ["ebin/escript_test_with_path"]) == {"TEST\n", 0} 219 end) 220 end 221 222 test "generate escript with deps" do 223 in_fixture("escript_test", fn -> 224 Mix.Project.push(EscriptWithDeps) 225 226 Mix.Tasks.Escript.Build.run([]) 227 228 message = "Generated escript escript_test_with_deps with MIX_ENV=dev" 229 assert_received {:mix_shell, :info, [^message]} 230 231 assert System.cmd("escript", ["escript_test_with_deps"]) == {"TEST\n", 0} 232 end) 233 after 234 purge([Ok.MixProject]) 235 end 236 237 test "generate escript with Erlang and deps" do 238 in_fixture("escript_test", fn -> 239 Mix.Project.push(EscriptErlangWithDeps) 240 241 Mix.Tasks.Escript.Build.run([]) 242 243 message = "Generated escript escript_test_erlang_with_deps with MIX_ENV=dev" 244 assert_received {:mix_shell, :info, [^message]} 245 246 assert System.cmd("escript", ["escript_test_erlang_with_deps", "arg1", "arg2"]) == 247 {~s(["arg1","arg2"]), 0} 248 end) 249 after 250 purge([Ok.MixProject]) 251 end 252 253 test "generate escript with Erlang main module" do 254 in_fixture("escript_test", fn -> 255 Mix.Project.push(EscriptErlangMainModule) 256 257 Mix.Tasks.Escript.Build.run([]) 258 259 message = "Generated escript escript_test_erlang_main_module with MIX_ENV=dev" 260 assert_received {:mix_shell, :info, [^message]} 261 262 assert System.cmd("escript", ["escript_test_erlang_main_module", "arg1", "arg2"]) == 263 {~s([<<"arg1">>,<<"arg2">>]), 0} 264 end) 265 after 266 purge([Ok.MixProject]) 267 end 268 269 test "generate escript with consolidated protocols" do 270 in_fixture("escript_test", fn -> 271 Mix.Project.push(EscriptConsolidated) 272 273 Mix.Tasks.Escript.Build.run([]) 274 275 message = "Generated escript escript_test_consolidated with MIX_ENV=dev" 276 assert_received {:mix_shell, :info, [^message]} 277 278 assert System.cmd("escript", ["escript_test_consolidated", "--protocol", "Enumerable"]) == 279 {"true\n", 0} 280 end) 281 end 282 283 test "escript install and uninstall" do 284 File.rm_rf!(tmp_path(".mix/escripts")) 285 286 in_fixture("escript_test", fn -> 287 Mix.Project.push(Escript) 288 289 # check that no escripts are installed 290 Mix.Tasks.Escript.run([]) 291 assert_received {:mix_shell, :info, ["No escripts currently installed."]} 292 293 # build and install our escript 294 send(self(), {:mix_shell_input, :yes?, true}) 295 Mix.Tasks.Escript.Install.run([]) 296 297 # check that it shows in the list 298 Mix.Tasks.Escript.run([]) 299 assert_received {:mix_shell, :info, ["* escript_test"]} 300 refute_received {:mix_shell, :info, ["* escript_test.bat"]} 301 302 # check uninstall confirmation 303 send(self(), {:mix_shell_input, :yes?, false}) 304 Mix.Tasks.Escript.Uninstall.run(["escript_test"]) 305 assert File.regular?(tmp_path(".mix/escripts/escript_test")) 306 307 # uninstall the escript 308 send(self(), {:mix_shell_input, :yes?, true}) 309 Mix.Tasks.Escript.Uninstall.run(["escript_test"]) 310 refute File.regular?(tmp_path(".mix/escripts/escript_test")) 311 refute File.regular?(tmp_path(".mix/escripts/escript_test.bat")) 312 313 # check that no escripts remain 314 Mix.Tasks.Escript.run([]) 315 assert_received {:mix_shell, :info, ["No escripts currently installed."]} 316 end) 317 end 318 319 test "escript install and uninstall --force" do 320 File.rm_rf!(tmp_path(".mix/escripts")) 321 322 in_fixture("escript_test", fn -> 323 Mix.Project.push(Escript) 324 325 Mix.Tasks.Escript.Install.run(["--force"]) 326 327 # check that it shows in the list 328 Mix.Tasks.Escript.run([]) 329 assert_received {:mix_shell, :info, ["* escript_test"]} 330 refute_received {:mix_shell, :info, ["* escript_test.bat"]} 331 332 # uninstall the escript 333 Mix.Tasks.Escript.Uninstall.run(["escript_test", "--force"]) 334 335 # check that no escripts remain 336 Mix.Tasks.Escript.run([]) 337 assert_received {:mix_shell, :info, ["No escripts currently installed."]} 338 end) 339 end 340 341 test "escript invalid install" do 342 # Install our escript 343 send(self(), {:mix_shell_input, :yes?, true}) 344 message = "The given path does not point to an escript, installation aborted" 345 346 assert_raise Mix.Error, message, fn -> 347 Mix.Tasks.Escript.Install.run([__ENV__.file]) 348 end 349 end 350 351 test "escript invalid main module" do 352 in_fixture("escript_test", fn -> 353 Mix.Project.push(EscriptWithUnknownMainModule) 354 355 message = 356 "Could not generate escript, module Elixir.BogusEscriptTest defined as :main_module could not be loaded" 357 358 assert_raise Mix.Error, message, fn -> 359 Mix.Tasks.Escript.Build.run([]) 360 end 361 end) 362 end 363 364 test "escript.install from Git" do 365 in_fixture("git_repo", fn -> 366 File.mkdir_p!("config") 367 368 File.write!("config/config.exs", """ 369 import Config 370 config :git_repo, :escript_config, true 371 """) 372 373 File.write!("lib/git_repo.ex", """ 374 require Application 375 true = Application.compile_env!(:git_repo, :escript_config) 376 377 defmodule GitRepo do 378 def main(_argv) do 379 IO.puts("TEST") 380 end 381 end 382 """) 383 384 File.write!("mix.exs", """ 385 defmodule GitRepo.MixProject do 386 use Mix.Project 387 388 def project do 389 [app: :git_repo, version: "0.1.0", escript: [main_module: GitRepo]] 390 end 391 end 392 """) 393 394 System.cmd("git", ~w[add .]) 395 System.cmd("git", ~w[commit -m "ok"]) 396 397 send(self(), {:mix_shell_input, :yes?, true}) 398 Mix.Tasks.Escript.Install.run(["git", File.cwd!()]) 399 assert_received {:mix_shell, :info, ["Generated escript git_repo with MIX_ENV=prod"]} 400 401 escript_path = Path.join([tmp_path(".mix"), "escripts", "git_repo"]) 402 assert System.cmd("escript", [escript_path]) == {"TEST\n", 0} 403 end) 404 after 405 purge([GitRepo, GitRepo.MixProject]) 406 end 407end 408