1Code.require_file("../test_helper.exs", __DIR__) 2 3defmodule Kernel.RaiseTest do 4 use ExUnit.Case, async: true 5 6 # Silence warnings 7 defp atom, do: RuntimeError 8 defp binary, do: "message" 9 defp opts, do: [message: "message"] 10 defp struct, do: %RuntimeError{message: "message"} 11 12 @compile {:no_warn_undefined, DoNotExist} 13 @trace [{:foo, :bar, 0, []}] 14 15 test "raise preserves the stacktrace" do 16 stacktrace = 17 try do 18 raise "a" 19 rescue 20 _ -> hd(__STACKTRACE__) 21 end 22 23 file = __ENV__.file |> Path.relative_to_cwd() |> String.to_charlist() 24 25 assert {__MODULE__, :"test raise preserves the stacktrace", _, [file: ^file, line: 18] ++ _} = 26 stacktrace 27 end 28 29 test "raise message" do 30 assert_raise RuntimeError, "message", fn -> 31 raise "message" 32 end 33 34 assert_raise RuntimeError, "message", fn -> 35 var = binary() 36 raise var 37 end 38 end 39 40 test "raise with no arguments" do 41 assert_raise RuntimeError, fn -> 42 raise RuntimeError 43 end 44 45 assert_raise RuntimeError, fn -> 46 var = atom() 47 raise var 48 end 49 end 50 51 test "raise with arguments" do 52 assert_raise RuntimeError, "message", fn -> 53 raise RuntimeError, message: "message" 54 end 55 56 assert_raise RuntimeError, "message", fn -> 57 atom = atom() 58 opts = opts() 59 raise atom, opts 60 end 61 end 62 63 test "raise existing exception" do 64 assert_raise RuntimeError, "message", fn -> 65 raise %RuntimeError{message: "message"} 66 end 67 68 assert_raise RuntimeError, "message", fn -> 69 var = struct() 70 raise var 71 end 72 end 73 74 if :erlang.system_info(:otp_release) >= '24' do 75 test "raise with error_info" do 76 {exception, stacktrace} = 77 try do 78 raise "a" 79 rescue 80 e -> {e, __STACKTRACE__} 81 end 82 83 assert [{__MODULE__, _, _, meta} | _] = stacktrace 84 assert meta[:error_info] == %{module: Exception} 85 86 assert Exception.format_error(exception, stacktrace) == 87 %{general: "a", reason: "#Elixir.RuntimeError"} 88 end 89 end 90 91 test "reraise message" do 92 try do 93 reraise "message", @trace 94 flunk("should not reach") 95 rescue 96 RuntimeError -> 97 assert @trace == __STACKTRACE__ 98 end 99 100 try do 101 var = binary() 102 reraise var, @trace 103 flunk("should not reach") 104 rescue 105 RuntimeError -> 106 assert @trace == __STACKTRACE__ 107 end 108 end 109 110 test "reraise with no arguments" do 111 try do 112 reraise RuntimeError, @trace 113 flunk("should not reach") 114 rescue 115 RuntimeError -> 116 assert @trace == __STACKTRACE__ 117 end 118 119 try do 120 var = atom() 121 reraise var, @trace 122 flunk("should not reach") 123 rescue 124 RuntimeError -> 125 assert @trace == __STACKTRACE__ 126 end 127 end 128 129 test "reraise with arguments" do 130 try do 131 reraise RuntimeError, [message: "message"], @trace 132 flunk("should not reach") 133 rescue 134 RuntimeError -> 135 assert @trace == __STACKTRACE__ 136 end 137 138 try do 139 atom = atom() 140 opts = opts() 141 reraise atom, opts, @trace 142 flunk("should not reach") 143 rescue 144 RuntimeError -> 145 assert @trace == __STACKTRACE__ 146 end 147 end 148 149 test "reraise existing exception" do 150 try do 151 reraise %RuntimeError{message: "message"}, @trace 152 flunk("should not reach") 153 rescue 154 RuntimeError -> 155 assert @trace == __STACKTRACE__ 156 end 157 158 try do 159 var = struct() 160 reraise var, @trace 161 flunk("should not reach") 162 rescue 163 RuntimeError -> 164 assert @trace == __STACKTRACE__ 165 end 166 end 167 168 test "reraise with invalid stacktrace" do 169 try do 170 reraise %RuntimeError{message: "message"}, {:oops, @trace} 171 rescue 172 ArgumentError -> 173 {name, arity} = __ENV__.function 174 assert [{__MODULE__, ^name, ^arity, _} | _] = __STACKTRACE__ 175 end 176 end 177 178 describe "rescue" do 179 test "runtime error" do 180 result = 181 try do 182 raise "an exception" 183 rescue 184 RuntimeError -> true 185 catch 186 :error, _ -> false 187 end 188 189 assert result 190 191 result = 192 try do 193 raise "an exception" 194 rescue 195 AnotherError -> true 196 catch 197 :error, _ -> false 198 end 199 200 refute result 201 end 202 203 test "named runtime error" do 204 result = 205 try do 206 raise "an exception" 207 rescue 208 x in [RuntimeError] -> Exception.message(x) 209 catch 210 :error, _ -> false 211 end 212 213 assert result == "an exception" 214 end 215 216 test "named runtime or argument error" do 217 result = 218 try do 219 raise "an exception" 220 rescue 221 x in [ArgumentError, RuntimeError] -> Exception.message(x) 222 catch 223 :error, _ -> false 224 end 225 226 assert result == "an exception" 227 end 228 229 test "with higher precedence than catch" do 230 result = 231 try do 232 raise "an exception" 233 rescue 234 _ -> true 235 catch 236 _, _ -> false 237 end 238 239 assert result 240 end 241 242 test "argument error from Erlang" do 243 result = 244 try do 245 :erlang.error(:badarg) 246 rescue 247 ArgumentError -> true 248 end 249 250 assert result 251 end 252 253 test "argument error from Elixir" do 254 result = 255 try do 256 raise ArgumentError, "" 257 rescue 258 ArgumentError -> true 259 end 260 261 assert result 262 end 263 264 test "catch-all variable" do 265 result = 266 try do 267 raise "an exception" 268 rescue 269 x -> Exception.message(x) 270 end 271 272 assert result == "an exception" 273 end 274 275 test "catch-all underscore" do 276 result = 277 try do 278 raise "an exception" 279 rescue 280 _ -> true 281 end 282 283 assert result 284 end 285 286 test "catch-all unused variable" do 287 result = 288 try do 289 raise "an exception" 290 rescue 291 _any -> true 292 end 293 294 assert result 295 end 296 297 test "catch-all with \"x in _\" syntax" do 298 result = 299 try do 300 raise "an exception" 301 rescue 302 exception in _ -> 303 Exception.message(exception) 304 end 305 306 assert result == "an exception" 307 end 308 end 309 310 describe "normalize" do 311 test "wrap custom Erlang error" do 312 result = 313 try do 314 :erlang.error(:sample) 315 rescue 316 x in [ErlangError] -> Exception.message(x) 317 end 318 319 assert result == "Erlang error: :sample" 320 end 321 322 test "undefined function error" do 323 result = 324 try do 325 DoNotExist.for_sure() 326 rescue 327 x in [UndefinedFunctionError] -> Exception.message(x) 328 end 329 330 assert result == 331 "function DoNotExist.for_sure/0 is undefined (module DoNotExist is not available)" 332 end 333 334 test "function clause error" do 335 result = 336 try do 337 zero(1) 338 rescue 339 x in [FunctionClauseError] -> Exception.message(x) 340 end 341 342 assert result == "no function clause matching in Kernel.RaiseTest.zero/1" 343 end 344 345 test "badarg error" do 346 result = 347 try do 348 :erlang.error(:badarg) 349 rescue 350 x in [ArgumentError] -> Exception.message(x) 351 end 352 353 assert result == "argument error" 354 end 355 356 test "tuple badarg error" do 357 result = 358 try do 359 :erlang.error({:badarg, [1, 2, 3]}) 360 rescue 361 x in [ArgumentError] -> Exception.message(x) 362 end 363 364 assert result == "argument error: [1, 2, 3]" 365 end 366 367 test "badarith error" do 368 result = 369 try do 370 :erlang.error(:badarith) 371 rescue 372 x in [ArithmeticError] -> Exception.message(x) 373 end 374 375 assert result == "bad argument in arithmetic expression" 376 end 377 378 test "badarity error" do 379 fun = fn x -> x end 380 string = "#{inspect(fun)} with arity 1 called with 2 arguments (1, 2)" 381 382 result = 383 try do 384 fun.(1, 2) 385 rescue 386 x in [BadArityError] -> Exception.message(x) 387 end 388 389 assert result == string 390 end 391 392 test "badfun error" do 393 # Avoid "invalid function call" warning 394 x = fn -> :example end 395 396 result = 397 try do 398 x.().(2) 399 rescue 400 x in [BadFunctionError] -> Exception.message(x) 401 end 402 403 assert result == "expected a function, got: :example" 404 end 405 406 test "badfun error when the function is gone" do 407 defmodule BadFunction.Missing do 408 def fun, do: fn -> :ok end 409 end 410 411 fun = BadFunction.Missing.fun() 412 413 :code.delete(BadFunction.Missing) 414 415 defmodule BadFunction.Missing do 416 def fun, do: fn -> :another end 417 end 418 419 :code.purge(BadFunction.Missing) 420 421 result = 422 try do 423 fun.() 424 rescue 425 x in [BadFunctionError] -> Exception.message(x) 426 end 427 428 assert result =~ 429 ~r/function #Function<[0-9]\.[0-9]*\/0 in Kernel.RaiseTest.BadFunction.Missing> is invalid, likely because it points to an old version of the code/ 430 end 431 432 test "badmatch error" do 433 x = :example 434 435 result = 436 try do 437 ^x = zero(0) 438 rescue 439 x in [MatchError] -> Exception.message(x) 440 end 441 442 assert result == "no match of right hand side value: 0" 443 end 444 445 defp empty_map(), do: %{} 446 447 test "bad key error" do 448 result = 449 try do 450 %{empty_map() | foo: :bar} 451 rescue 452 x in [KeyError] -> Exception.message(x) 453 end 454 455 assert result == "key :foo not found" 456 457 result = 458 try do 459 empty_map().foo 460 rescue 461 x in [KeyError] -> Exception.message(x) 462 end 463 464 assert result == "key :foo not found in: %{}" 465 end 466 467 test "bad map error" do 468 result = 469 try do 470 %{zero(0) | foo: :bar} 471 rescue 472 x in [BadMapError] -> Exception.message(x) 473 end 474 475 assert result == "expected a map, got: 0" 476 end 477 478 test "bad boolean error" do 479 result = 480 try do 481 1 and true 482 rescue 483 x in [BadBooleanError] -> Exception.message(x) 484 end 485 486 assert result == "expected a boolean on left-side of \"and\", got: 1" 487 end 488 489 test "case clause error" do 490 x = :example 491 492 result = 493 try do 494 case zero(0) do 495 ^x -> nil 496 end 497 rescue 498 x in [CaseClauseError] -> Exception.message(x) 499 end 500 501 assert result == "no case clause matching: 0" 502 end 503 504 test "cond clause error" do 505 result = 506 try do 507 cond do 508 !zero(0) -> :ok 509 end 510 rescue 511 x in [CondClauseError] -> Exception.message(x) 512 end 513 514 assert result == "no cond clause evaluated to a truthy value" 515 end 516 517 test "try clause error" do 518 f = fn -> :example end 519 520 result = 521 try do 522 try do 523 f.() 524 rescue 525 _exception -> 526 :ok 527 else 528 :other -> 529 :ok 530 end 531 rescue 532 x in [TryClauseError] -> Exception.message(x) 533 end 534 535 assert result == "no try clause matching: :example" 536 end 537 538 test "undefined function error as Erlang error" do 539 result = 540 try do 541 DoNotExist.for_sure() 542 rescue 543 x in [ErlangError] -> Exception.message(x) 544 end 545 546 assert result == 547 "function DoNotExist.for_sure/0 is undefined (module DoNotExist is not available)" 548 end 549 end 550 551 defmacrop exceptions do 552 [ErlangError] 553 end 554 555 test "with macros" do 556 result = 557 try do 558 DoNotExist.for_sure() 559 rescue 560 x in exceptions() -> Exception.message(x) 561 end 562 563 assert result == 564 "function DoNotExist.for_sure/0 is undefined (module DoNotExist is not available)" 565 end 566 567 defp zero(0), do: 0 568end 569