Current section
15 Versions
Jump to
Current section
15 Versions
Compare versions
4
files changed
+93
additions
-20
deletions
| @@ -15,4 +15,4 @@ | |
| 15 15 | {<<"maintainers">>,[<<"Takayuki Matsubara">>]}. |
| 16 16 | {<<"name">>,<<"power_assert">>}. |
| 17 17 | {<<"requirements">>,[]}. |
| 18 | - {<<"version">>,<<"0.0.5">>}. |
| 18 | + {<<"version">>,<<"0.0.6">>}. |
| @@ -4,6 +4,7 @@ defmodule PowerAssert.Assertion do | |
| 4 4 | """ |
| 5 5 | |
| 6 6 | @assign_len 3 # length of " = " |
| 7 | + @equal_len 4 # length of " == " |
| 7 8 | |
| 8 9 | @doc """ |
| 9 10 | assert with descriptive messages |
| @@ -15,11 +16,16 @@ defmodule PowerAssert.Assertion do | |
| 15 16 | | 3 |
| 16 17 | [1, 2, 3] |
| 17 18 | """ |
| 18 | - defmacro assert({:=, _, [left, right]} = ast) do |
| 19 | + defmacro assert(ast, msg \\ nil) |
| 20 | + |
| 21 | + defmacro assert({:=, _, [left, right]} = ast, msg) do |
| 19 22 | # Almost the same code as ExUnit but rhs is displayed in detail |
| 20 23 | code = Macro.escape(ast) |
| 24 | + [_|t] = String.split(Macro.to_string(ast), " = ") |
| 25 | + rhs_expr = Enum.join(t, " = ") |
| 21 26 | rhs_index = (Macro.to_string(left) |> String.length) + @assign_len |
| 22 | - injected_rhs_ast = inject_store_code(right, rhs_index) |
| 27 | + injected_rhs_ast = inject_store_code(right, rhs_expr, rhs_index) |
| 28 | + message_ast = message_ast(msg) |
| 23 29 | |
| 24 30 | {:if, meta, args} = |
| 25 31 | quote do |
| @@ -27,6 +33,7 @@ defmodule PowerAssert.Assertion do | |
| 27 33 | right |
| 28 34 | else |
| 29 35 | message = PowerAssert.Assertion.render_values(expr, values) |
| 36 | + unquote(message_ast) |
| 30 37 | raise ExUnit.AssertionError, |
| 31 38 | message: "Expected truthy, got #{inspect right}\n\n" <> message |
| 32 39 | end |
| @@ -40,6 +47,7 @@ defmodule PowerAssert.Assertion do | |
| 40 47 | unquote(return) |
| 41 48 | _ -> |
| 42 49 | message = PowerAssert.Assertion.render_values(expr, values) |
| 50 | + unquote(message_ast) |
| 43 51 | raise ExUnit.AssertionError, |
| 44 52 | message: message |
| 45 53 | end |
| @@ -53,18 +61,36 @@ defmodule PowerAssert.Assertion do | |
| 53 61 | end |
| 54 62 | end |
| 55 63 | |
| 56 | - defmacro assert(ast, msg \\ nil) do |
| 64 | + defmacro assert({:==, _, [left, right]} = ast, msg) do |
| 57 65 | code = Macro.escape(ast) |
| 58 | - injected_ast = inject_store_code(ast) |
| 66 | + [lhs_expr|t] = String.split(Macro.to_string(ast), " == ") |
| 67 | + rhs_expr = Enum.join(t, " == ") |
| 68 | + injected_lhs_ast = inject_store_code(left, lhs_expr) |
| 69 | + rhs_index = (Macro.to_string(left) |> String.length) + @equal_len |
| 70 | + injected_rhs_ast = inject_store_code(right, rhs_expr, rhs_index) |
| 71 | + message_ast = message_ast(msg) |
| 59 72 | |
| 60 | - # for avoid "this check/guard will always yield the same result" |
| 61 | - message_ast = if msg do |
| 62 73 | quote do |
| 63 | - message = "#{unquote(msg)}\n\n" <> message |
| 74 | + unquote(injected_lhs_ast) |
| 75 | + left = result |
| 76 | + left_values = values |
| 77 | + unquote(injected_rhs_ast) |
| 78 | + # wrap result for avoid warning: this check/guard will always yield the same result |
| 79 | + unless left == fn(x) -> x end.(result) do |
| 80 | + message = PowerAssert.Assertion.render_values(unquote(code), left_values ++ values, left, result) |
| 81 | + unquote(message_ast) |
| 82 | + raise ExUnit.AssertionError, |
| 83 | + message: message |
| 64 84 | end |
| 65 | - else |
| 66 | - nil |
| 85 | + result |
| 67 86 | end |
| 87 | + end |
| 88 | + |
| 89 | + defmacro assert(ast, msg) do |
| 90 | + code = Macro.escape(ast) |
| 91 | + injected_ast = inject_store_code(ast, Macro.to_string(ast)) |
| 92 | + |
| 93 | + message_ast = message_ast(msg) |
| 68 94 | |
| 69 95 | quote do |
| 70 96 | unquote(injected_ast) |
| @@ -78,9 +104,17 @@ defmodule PowerAssert.Assertion do | |
| 78 104 | end |
| 79 105 | end |
| 80 106 | |
| 107 | + # for avoid "this check/guard will always yield the same result" |
| 108 | + defp message_ast(msg) when is_binary(msg) do |
| 109 | + quote do |
| 110 | + message = "#{unquote(msg)}\n\n" <> message |
| 111 | + end |
| 112 | + end |
| 113 | + defp message_ast(_msg), do: nil |
| 114 | + |
| 81 115 | @doc false |
| 82 | - def inject_store_code(ast, default_index \\ 0) do |
| 83 | - positions = detect_position(ast, default_index) |
| 116 | + def inject_store_code(ast, expr, default_index \\ 0) do |
| 117 | + positions = detect_position(ast, expr, default_index) |
| 84 118 | {injected_ast, {_, _}} = PowerAssert.Ast.traverse(ast, {Enum.reverse(positions), 0}, &pre_catcher/2, &catcher/2) |
| 85 119 | # IO.inspect injected_ast |
| 86 120 | # IO.inspect Macro.to_string injected_ast |
| @@ -93,9 +127,9 @@ defmodule PowerAssert.Assertion do | |
| 93 127 | end |
| 94 128 | |
| 95 129 | ## detect positions |
| 96 | - defp detect_position(ast, default_index) do |
| 130 | + defp detect_position(ast, expr, default_index) do |
| 97 131 | {_ast, {_code, positions, _in_fn}} = |
| 98 | - PowerAssert.Ast.traverse(ast, {Macro.to_string(ast), [], 0}, &pre_collect_position/2, &collect_position/2) |
| 132 | + PowerAssert.Ast.traverse(ast, {expr, [], 0}, &pre_collect_position/2, &collect_position/2) |
| 99 133 | if default_index != 0 do |
| 100 134 | positions = Enum.map(positions, fn([pos, code]) -> [default_index + pos, code] end) |
| 101 135 | end |
| @@ -397,16 +431,17 @@ defmodule PowerAssert.Assertion do | |
| 397 431 | |
| 398 432 | |
| 399 433 | ## render |
| 400 | - def render_values(code, []) do |
| 401 | - Macro.to_string(code) |
| 434 | + def render_values(code, values, left \\ nil, right \\ nil) |
| 435 | + def render_values(code, [], left, right) do |
| 436 | + Macro.to_string(code) <> extra_information(left, right) |
| 402 437 | end |
| 403 | - def render_values(code, values) do |
| 438 | + def render_values(code, values, left, right) do |
| 404 439 | code_str = Macro.to_string(code) |
| 405 440 | values = Enum.sort(values, fn([x_pos, _], [y_pos, _]) -> x_pos > y_pos end) |
| 406 441 | [max_pos, _] = Enum.max_by(values, fn ([pos, _]) -> pos end) |
| 407 442 | first_line = String.duplicate(" ", max_pos + 1) |> replace_with_bar(values) |
| 408 443 | lines = make_lines([], Enum.count(values), values, -1) |
| 409 | - Enum.join([code_str, first_line] ++ lines, "\n") |
| 444 | + Enum.join([code_str, first_line] ++ lines, "\n") <> extra_information(left, right) |
| 410 445 | end |
| 411 446 | |
| 412 447 | defp make_lines(lines, 0, _, _latest_pos) do |
| @@ -437,4 +472,42 @@ defmodule PowerAssert.Assertion do | |
| 437 472 | String.replace(front, ~r/ $/, "|") <> back |
| 438 473 | end) |
| 439 474 | end |
| 475 | + |
| 476 | + defp extra_information(left, right) when is_list(left) and is_list(right) do |
| 477 | + ["\n\nonly in lhs: " <> ((left -- right) |> inspect), |
| 478 | + "only in rhs: " <> ((right -- left) |> inspect)] |
| 479 | + |> Enum.join("\n") |
| 480 | + end |
| 481 | + defp extra_information(left, right) when is_map(left) and is_map(right) do |
| 482 | + if Map.has_key? left, :__struct__ do |
| 483 | + left = Map.from_struct(left) |
| 484 | + right = Map.from_struct(right) |
| 485 | + end |
| 486 | + in_left = Map.split(left, Map.keys(right)) |> elem(1) |
| 487 | + in_right = Map.split(right, Map.keys(left)) |> elem(1) |
| 488 | + str = "\n" |
| 489 | + unless Map.size(in_left) == 0 do |
| 490 | + str = str <> "\nonly in lhs: " <> inspect(in_left) |
| 491 | + end |
| 492 | + unless Map.size(in_right) == 0 do |
| 493 | + str = str <> "\nonly in rhs: " <> inspect(in_right) |
| 494 | + end |
| 495 | + diff = collect_map_diff(left, right) |
| 496 | + unless Enum.empty?(diff) do |
| 497 | + str = str <> "\ndifference:\n" <> Enum.join(diff, "\n") |
| 498 | + end |
| 499 | + str |
| 500 | + end |
| 501 | + defp extra_information(_left, _right), do: "" |
| 502 | + |
| 503 | + defp collect_map_diff(map1, map2) do |
| 504 | + Enum.reduce(map2, [], fn({k, v}, acc) -> |
| 505 | + case Map.fetch(map1, k) do |
| 506 | + {:ok, ^v} -> acc |
| 507 | + {:ok, map1_value} -> |
| 508 | + acc ++ ["key #{inspect k} => {#{inspect map1_value}, #{inspect v}}"] |
| 509 | + _ -> acc |
| 510 | + end |
| 511 | + end) |
| 512 | + end |
| 440 513 | end |
| @@ -18,7 +18,7 @@ defmodule PowerAssert.Debug do | |
| 18 18 | """ |
| 19 19 | defmacro puts_expr(ast) do |
| 20 20 | code = Macro.escape(ast) |
| 21 | - injected_ast = inject_store_code(ast) |
| 21 | + injected_ast = inject_store_code(ast, Macro.to_string(ast)) |
| 22 22 | |
| 23 23 | quote do |
| 24 24 | unquote(injected_ast) |
| @@ -3,7 +3,7 @@ defmodule PowerAssert.Mixfile do | |
| 3 3 | |
| 4 4 | def project do |
| 5 5 | [app: :power_assert, |
| 6 | - version: "0.0.5", |
| 6 | + version: "0.0.6", |
| 7 7 | elixir: "~> 1.0", |
| 8 8 | description: "Power Assert in Elixir", |
| 9 9 | package: [ |