From 68ff9b24b320c6d14ee064b8afbc279e52cafe75 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 01:37:35 +0100 Subject: [PATCH 1/9] escape map --- lib/ex_unit/lib/ex_unit/diff.ex | 11 ++++- lib/ex_unit/test/ex_unit/formatter_test.exs | 52 +++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 05c2c7fda7e..7467f571c40 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -558,7 +558,7 @@ defmodule ExUnit.Diff do end defp move_right({y, list1, [elem2 | rest2], {edit1, edit2, env}}) do - {y, list1, rest2, {edit1, [{:ins, elem2} | edit2], env}} + {y, list1, rest2, {edit1, [{:ins, build_elem(elem2)} | edit2], env}} end defp move_right({y, list1, [], edits}) do @@ -566,7 +566,7 @@ defmodule ExUnit.Diff do end defp move_down({y, [elem1 | rest1], list2, {edit1, edit2, env}}) do - {y + 1, rest1, list2, {[{:del, elem1} | edit1], edit2, env}} + {y + 1, rest1, list2, {[{:del, build_elem(elem1)} | edit1], edit2, env}} end defp move_down({y, [], list2, edits}) do @@ -763,6 +763,10 @@ defmodule ExUnit.Diff do _ -> :error end + def build_elem(elem) when is_struct(elem), do: elem + def build_elem(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} + def build_elem(elem), do: elem + defp maybe_struct(%name{}), do: name defp maybe_struct(_), do: nil @@ -1145,6 +1149,9 @@ defmodule ExUnit.Diff do # We escape container types to make a distinction between AST # and values that should be inspected. All other values have no # special AST representation, so we can keep them as is. + # Maps should be formatted not inspected. + defp escape(other) when is_struct(other), do: {other} + defp escape({:%{}, _, _} = other), do: other defp escape(other) when is_list(other) or is_tuple(other), do: {other} defp escape(other), do: other diff --git a/lib/ex_unit/test/ex_unit/formatter_test.exs b/lib/ex_unit/test/ex_unit/formatter_test.exs index a1e5506c1f6..68f0199a0e3 100644 --- a/lib/ex_unit/test/ex_unit/formatter_test.exs +++ b/lib/ex_unit/test/ex_unit/formatter_test.exs @@ -469,6 +469,58 @@ defmodule ExUnit.FormatterTest do """ end + test "formats nested maps with column limit" do + map = + %{ + microsecond: {1, 2}, + second: 3, + month: 4, + day: 5, + year: 2024, + minute: 6, + hour: 7 + } + + failure = [{:error, catch_assertion(assert [map, %{map | hour: 8}] == [map]), []}] + + assert format_test_all_failure(test_module(), failure, 1, 80, &formatter/2) == """ + 1) Hello: failure on setup_all callback, all tests have been invalidated + Assertion with == failed + code: assert [map, %{map | hour: 8}] == [map] + left: [ + %{ + microsecond: {1, 2}, + second: 3, + month: 4, + day: 5, + year: 2024, + minute: 6, + hour: 7 + }, + %{ + microsecond: {1, 2}, + second: 3, + month: 4, + day: 5, + year: 2024, + minute: 6, + hour: 8 + } + ] + right: [ + %{ + microsecond: {1, 2}, + second: 3, + month: 4, + day: 5, + year: 2024, + minute: 6, + hour: 7 + } + ] + """ + end + test "formats assertions with complex function call arguments" do failure = [{:error, catch_assertion(assert is_list(List.to_tuple([1, 2, 3]))), []}] From ce0577d57f5b3d3893bfbb00c86bdc8f62263697 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 10:24:43 +0100 Subject: [PATCH 2/9] move tests --- lib/ex_unit/lib/ex_unit/diff.ex | 10 +- lib/ex_unit/test/ex_unit/diff_test.exs | 108 +++++++++++++++++++- lib/ex_unit/test/ex_unit/formatter_test.exs | 52 ---------- 3 files changed, 111 insertions(+), 59 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 7467f571c40..c1fbbd155ea 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -398,7 +398,13 @@ defmodule ExUnit.Diff do defp diff_improper([], right, env) when is_list(right) do equivalent? = right == [] - right = right |> escape() |> update_diff_meta(not equivalent?) + + right = + right + |> Enum.map(&build_elem/1) + |> escape() + |> update_diff_meta(not equivalent?) + {%__MODULE__{equivalent?: equivalent?, right: right, left: []}, env} end @@ -558,7 +564,7 @@ defmodule ExUnit.Diff do end defp move_right({y, list1, [elem2 | rest2], {edit1, edit2, env}}) do - {y, list1, rest2, {edit1, [{:ins, build_elem(elem2)} | edit2], env}} + {y, list1, rest2, {edit1, [{:ins, elem2} | edit2], env}} end defp move_right({y, list1, [], edits}) do diff --git a/lib/ex_unit/test/ex_unit/diff_test.exs b/lib/ex_unit/test/ex_unit/diff_test.exs index 04340b8db5a..4e9a9f743f0 100644 --- a/lib/ex_unit/test/ex_unit/diff_test.exs +++ b/lib/ex_unit/test/ex_unit/diff_test.exs @@ -127,9 +127,11 @@ defmodule ExUnit.DiffTest do end defp to_diff(side, sign) do + terminal_width = 80 + side |> Diff.to_algebra(&diff_wrapper(&1, sign)) - |> Algebra.format(:infinity) + |> Algebra.format(terminal_width) |> IO.iodata_to_binary() end @@ -682,13 +684,49 @@ defmodule ExUnit.DiffTest do refute_diff( %User{age: %Date{}} = struct, ~s/%ExUnit.DiffTest.User{age: %-Date-{}}/, - ~s/%ExUnit.DiffTest.User{age: %+DateTime+{calendar: Calendar.ISO, day: 30, hour: 13, microsecond: {253158, 6}, minute: 49, month: 7, second: 59, std_offset: 0, time_zone: "Etc\/UTC", utc_offset: 0, year: 2020, zone_abbr: "UTC"}, name: nil}/ + """ + %ExUnit.DiffTest.User{ + age: %+DateTime+{ + calendar: Calendar.ISO, + day: 30, + hour: 13, + microsecond: {253158, 6}, + minute: 49, + month: 7, + second: 59, + std_offset: 0, + time_zone: "Etc\/UTC", + utc_offset: 0, + year: 2020, + zone_abbr: "UTC" + }, + name: nil + }\ + """ ) refute_diff( %{age: %Date{}} = struct, ~s/%{age: %-Date-{}}/, - ~s/%ExUnit.DiffTest.User{age: %+DateTime+{calendar: Calendar.ISO, day: 30, hour: 13, microsecond: {253158, 6}, minute: 49, month: 7, second: 59, std_offset: 0, time_zone: "Etc\/UTC", utc_offset: 0, year: 2020, zone_abbr: "UTC"}, name: nil}/ + """ + %ExUnit.DiffTest.User{ + age: %+DateTime+{ + calendar: Calendar.ISO, + day: 30, + hour: 13, + microsecond: {253158, 6}, + minute: 49, + month: 7, + second: 59, + std_offset: 0, + time_zone: \"Etc/UTC\", + utc_offset: 0, + year: 2020, + zone_abbr: \"UTC\" + }, + name: nil + }\ + """ ) end @@ -836,6 +874,56 @@ defmodule ExUnit.DiffTest do ) end + test "maps in lists" do + map = %{ + first_name: "John", + last_name: "Doe", + age: 30, + notifications: true, + language: "en-US", + address: %{ + street: "123 Main St", + city: "Springfield", + state: "IL", + zip: "62701" + } + } + + refute_diff( + [map] == [], + """ + [ + -%{ + address: %{state: \"IL\", zip: \"62701\", street: \"123 Main St\", city: \"Springfield\"}, + age: 30, + first_name: \"John\", + last_name: \"Doe\", + notifications: true, + language: \"en-US\" + }- + ]\ + """, + "[]" + ) + + refute_diff( + [] == [map], + "[]", + """ + [ + +%{ + address: %{state: \"IL\", zip: \"62701\", street: \"123 Main St\", city: \"Springfield\"}, + age: 30, + first_name: \"John\", + last_name: \"Doe\", + notifications: true, + language: \"en-US\" + }+ + ]\ + """ + ) + end + test "maps and structs with escaped values" do refute_diff( %User{age: {1, 2, 3}} = %User{age: {1, 2, 4}}, @@ -1082,8 +1170,18 @@ defmodule ExUnit.DiffTest do refute_diff( {ref1, ref2} == {ref2, ref1}, - "{-#{inspect_ref1}-, -#{inspect_ref2}-}", - "{+#{inspect_ref2}+, +#{inspect_ref1}+}" + """ + { + -#{inspect_ref1}-, + -#{inspect_ref2}- + }\ + """, + """ + { + +#{inspect_ref2}+, + +#{inspect_ref1}+ + }\ + """ ) refute_diff( diff --git a/lib/ex_unit/test/ex_unit/formatter_test.exs b/lib/ex_unit/test/ex_unit/formatter_test.exs index 68f0199a0e3..a1e5506c1f6 100644 --- a/lib/ex_unit/test/ex_unit/formatter_test.exs +++ b/lib/ex_unit/test/ex_unit/formatter_test.exs @@ -469,58 +469,6 @@ defmodule ExUnit.FormatterTest do """ end - test "formats nested maps with column limit" do - map = - %{ - microsecond: {1, 2}, - second: 3, - month: 4, - day: 5, - year: 2024, - minute: 6, - hour: 7 - } - - failure = [{:error, catch_assertion(assert [map, %{map | hour: 8}] == [map]), []}] - - assert format_test_all_failure(test_module(), failure, 1, 80, &formatter/2) == """ - 1) Hello: failure on setup_all callback, all tests have been invalidated - Assertion with == failed - code: assert [map, %{map | hour: 8}] == [map] - left: [ - %{ - microsecond: {1, 2}, - second: 3, - month: 4, - day: 5, - year: 2024, - minute: 6, - hour: 7 - }, - %{ - microsecond: {1, 2}, - second: 3, - month: 4, - day: 5, - year: 2024, - minute: 6, - hour: 8 - } - ] - right: [ - %{ - microsecond: {1, 2}, - second: 3, - month: 4, - day: 5, - year: 2024, - minute: 6, - hour: 7 - } - ] - """ - end - test "formats assertions with complex function call arguments" do failure = [{:error, catch_assertion(assert is_list(List.to_tuple([1, 2, 3]))), []}] From fac970745c981a7b54d6c127960bac0c802143d8 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 10:55:24 +0100 Subject: [PATCH 3/9] rename to build_quoted and move --- lib/ex_unit/lib/ex_unit/diff.ex | 14 +++++++------- lib/ex_unit/test/ex_unit/diff_test.exs | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index c1fbbd155ea..280e52b68a3 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -401,7 +401,7 @@ defmodule ExUnit.Diff do right = right - |> Enum.map(&build_elem/1) + |> Enum.map(&build_quoted/1) |> escape() |> update_diff_meta(not equivalent?) @@ -572,7 +572,7 @@ defmodule ExUnit.Diff do end defp move_down({y, [elem1 | rest1], list2, {edit1, edit2, env}}) do - {y + 1, rest1, list2, {[{:del, build_elem(elem1)} | edit1], edit2, env}} + {y + 1, rest1, list2, {[{:del, elem1} | edit1], edit2, env}} end defp move_down({y, [], list2, edits}) do @@ -624,12 +624,12 @@ defmodule ExUnit.Diff do end defp list_script_to_diff([{:del, elem1} | rest1], rest2, _, left, right, env) do - diff_left = elem1 |> maybe_escape(env) |> update_diff_meta(true) + diff_left = elem1 |> build_quoted() |> maybe_escape(env) |> update_diff_meta(true) list_script_to_diff(rest1, rest2, false, [diff_left | left], right, env) end defp list_script_to_diff(rest1, [{:ins, elem2} | rest2], _, left, right, env) do - diff_right = elem2 |> escape() |> update_diff_meta(true) + diff_right = elem2 |> build_quoted() |> escape() |> update_diff_meta(true) list_script_to_diff(rest1, rest2, false, left, [diff_right | right], env) end @@ -769,9 +769,9 @@ defmodule ExUnit.Diff do _ -> :error end - def build_elem(elem) when is_struct(elem), do: elem - def build_elem(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} - def build_elem(elem), do: elem + def build_quoted(elem) when is_struct(elem), do: elem + def build_quoted(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} + def build_quoted(elem), do: elem defp maybe_struct(%name{}), do: name defp maybe_struct(_), do: nil diff --git a/lib/ex_unit/test/ex_unit/diff_test.exs b/lib/ex_unit/test/ex_unit/diff_test.exs index 4e9a9f743f0..0bed2bb857b 100644 --- a/lib/ex_unit/test/ex_unit/diff_test.exs +++ b/lib/ex_unit/test/ex_unit/diff_test.exs @@ -922,6 +922,8 @@ defmodule ExUnit.DiffTest do ]\ """ ) + + assert_diff([map] == [map], []) end test "maps and structs with escaped values" do From a3bf0b8f1f434b307b619e6330f44ccb92d9429e Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 11:05:42 +0100 Subject: [PATCH 4/9] move to escape function --- lib/ex_unit/lib/ex_unit/diff.ex | 13 ++++--------- lib/ex_unit/test/ex_unit/diff_test.exs | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 280e52b68a3..0110bf55f1e 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -398,12 +398,7 @@ defmodule ExUnit.Diff do defp diff_improper([], right, env) when is_list(right) do equivalent? = right == [] - - right = - right - |> Enum.map(&build_quoted/1) - |> escape() - |> update_diff_meta(not equivalent?) + right = right |> escape() |> update_diff_meta(not equivalent?) {%__MODULE__{equivalent?: equivalent?, right: right, left: []}, env} end @@ -624,12 +619,12 @@ defmodule ExUnit.Diff do end defp list_script_to_diff([{:del, elem1} | rest1], rest2, _, left, right, env) do - diff_left = elem1 |> build_quoted() |> maybe_escape(env) |> update_diff_meta(true) + diff_left = elem1 |> maybe_escape(env) |> update_diff_meta(true) list_script_to_diff(rest1, rest2, false, [diff_left | left], right, env) end defp list_script_to_diff(rest1, [{:ins, elem2} | rest2], _, left, right, env) do - diff_right = elem2 |> build_quoted() |> escape() |> update_diff_meta(true) + diff_right = elem2 |> escape() |> update_diff_meta(true) list_script_to_diff(rest1, rest2, false, left, [diff_right | right], env) end @@ -1157,7 +1152,7 @@ defmodule ExUnit.Diff do # special AST representation, so we can keep them as is. # Maps should be formatted not inspected. defp escape(other) when is_struct(other), do: {other} - defp escape({:%{}, _, _} = other), do: other + defp escape(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} defp escape(other) when is_list(other) or is_tuple(other), do: {other} defp escape(other), do: other diff --git a/lib/ex_unit/test/ex_unit/diff_test.exs b/lib/ex_unit/test/ex_unit/diff_test.exs index 0bed2bb857b..27a84bc7226 100644 --- a/lib/ex_unit/test/ex_unit/diff_test.exs +++ b/lib/ex_unit/test/ex_unit/diff_test.exs @@ -1200,7 +1200,16 @@ defmodule ExUnit.DiffTest do refute_diff(ref1 == :a, "-#{inspect_ref1}-", "+:a+") refute_diff({ref1, ref2} == :a, "-{#{inspect_ref1}, #{inspect_ref2}}", "+:a+") - refute_diff(%{ref1 => ref2} == :a, "-%{#{inspect_ref1} => #{inspect_ref2}}", "+:a+") + + refute_diff( + %{ref1 => ref2} == :a, + """ + -%{ + #{inspect_ref1} => #{inspect_ref2} + }\ + """, + "+:a+" + ) refute_diff( %Opaque{data: ref1} == :a, @@ -1241,7 +1250,16 @@ defmodule ExUnit.DiffTest do refute_diff(identity == :a, "-#{inspect}-", "+:a+") refute_diff({identity, identity} == :a, "-{#{inspect}, #{inspect}}", "+:a+") refute_diff({identity, :a} == {:a, identity}, "{-#{inspect}-, -:a-}", "{+:a+, +#{inspect}+}") - refute_diff(%{identity => identity} == :a, "-%{#{inspect} => #{inspect}}", "+:a+") + + refute_diff( + %{identity => identity} == :a, + """ + -%{ + #{inspect} => #{inspect} + }-\ + """, + "+:a+" + ) refute_diff( (&String.to_charlist/1) == (&String.unknown/1), From 38a8ae9210eeffe666c19419ae16322f4e394a85 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 11:07:49 +0100 Subject: [PATCH 5/9] cleanup --- lib/ex_unit/lib/ex_unit/diff.ex | 5 +---- lib/ex_unit/test/ex_unit/diff_test.exs | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 0110bf55f1e..cde5a2f2d00 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -398,6 +398,7 @@ defmodule ExUnit.Diff do defp diff_improper([], right, env) when is_list(right) do equivalent? = right == [] + right = right |> escape() |> update_diff_meta(not equivalent?) {%__MODULE__{equivalent?: equivalent?, right: right, left: []}, env} @@ -764,10 +765,6 @@ defmodule ExUnit.Diff do _ -> :error end - def build_quoted(elem) when is_struct(elem), do: elem - def build_quoted(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} - def build_quoted(elem), do: elem - defp maybe_struct(%name{}), do: name defp maybe_struct(_), do: nil diff --git a/lib/ex_unit/test/ex_unit/diff_test.exs b/lib/ex_unit/test/ex_unit/diff_test.exs index 27a84bc7226..97efb20493e 100644 --- a/lib/ex_unit/test/ex_unit/diff_test.exs +++ b/lib/ex_unit/test/ex_unit/diff_test.exs @@ -126,12 +126,11 @@ defmodule ExUnit.DiffTest do assert env_binding == expected_binding end + @terminal_width 80 defp to_diff(side, sign) do - terminal_width = 80 - side |> Diff.to_algebra(&diff_wrapper(&1, sign)) - |> Algebra.format(terminal_width) + |> Algebra.format(@terminal_width) |> IO.iodata_to_binary() end From 21df6c768b4bf20df09c6bfa805b734c8e6b4b42 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 11:08:38 +0100 Subject: [PATCH 6/9] cleanup again --- lib/ex_unit/lib/ex_unit/diff.ex | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index cde5a2f2d00..5513654b182 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -398,9 +398,7 @@ defmodule ExUnit.Diff do defp diff_improper([], right, env) when is_list(right) do equivalent? = right == [] - right = right |> escape() |> update_diff_meta(not equivalent?) - {%__MODULE__{equivalent?: equivalent?, right: right, left: []}, env} end From b694c9bab1c0e92c867566ebc166f0b580251bb5 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 11:27:26 +0100 Subject: [PATCH 7/9] don't wrap structs in tuple --- lib/ex_unit/lib/ex_unit/diff.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 5513654b182..ed0e33c95aa 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -1146,7 +1146,7 @@ defmodule ExUnit.Diff do # and values that should be inspected. All other values have no # special AST representation, so we can keep them as is. # Maps should be formatted not inspected. - defp escape(other) when is_struct(other), do: {other} + defp escape(other) when is_struct(other), do: other defp escape(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} defp escape(other) when is_list(other) or is_tuple(other), do: {other} defp escape(other), do: other From ae322db17d8c562e301aab288862066f800f861a Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 11:59:14 +0100 Subject: [PATCH 8/9] update comment --- lib/ex_unit/lib/ex_unit/diff.ex | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index ed0e33c95aa..31d4b86e920 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -1142,12 +1142,11 @@ defmodule ExUnit.Diff do defp maybe_escape(other, %{context: :match}), do: other defp maybe_escape(other, _env), do: escape(other) - # We escape container types to make a distinction between AST - # and values that should be inspected. All other values have no - # special AST representation, so we can keep them as is. - # Maps should be formatted not inspected. + # We escape container types to make a distinction between AST and values that + # should be inspected. Maps should not be inspected, convert it to ast. + # All other values have no special AST representation, so we can keep them as is. defp escape(other) when is_struct(other), do: other - defp escape(elem) when is_map(elem), do: {:%{}, [], Map.to_list(elem)} + defp escape(other) when is_map(other), do: {:%{}, [], Map.to_list(other)} defp escape(other) when is_list(other) or is_tuple(other), do: {other} defp escape(other), do: other From ef7fd405c9194344718a999f8c70af2eccd87812 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 3 Nov 2024 20:22:50 +0100 Subject: [PATCH 9/9] implement for structs --- lib/ex_unit/lib/ex_unit/diff.ex | 17 ++++- lib/ex_unit/test/ex_unit/diff_test.exs | 88 +++++++++++++++++++++----- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 31d4b86e920..f784d1a60b4 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -1143,10 +1143,21 @@ defmodule ExUnit.Diff do defp maybe_escape(other, _env), do: escape(other) # We escape container types to make a distinction between AST and values that - # should be inspected. Maps should not be inspected, convert it to ast. + # should be inspected. Maps and structs without custom inspect implementation + # should not be inspected, convert it to ast. # All other values have no special AST representation, so we can keep them as is. - defp escape(other) when is_struct(other), do: other - defp escape(other) when is_map(other), do: {:%{}, [], Map.to_list(other)} + defp escape(other) when is_map(other) do + struct = maybe_struct(other) + + if struct && Inspect.impl_for(other) not in [Inspect.Any, Inspect.Map] do + other + else + other + |> Map.to_list() + |> build_map_or_struct(struct) + end + end + defp escape(other) when is_list(other) or is_tuple(other), do: {other} defp escape(other), do: other diff --git a/lib/ex_unit/test/ex_unit/diff_test.exs b/lib/ex_unit/test/ex_unit/diff_test.exs index 97efb20493e..93d34d5cee0 100644 --- a/lib/ex_unit/test/ex_unit/diff_test.exs +++ b/lib/ex_unit/test/ex_unit/diff_test.exs @@ -10,6 +10,10 @@ defmodule ExUnit.DiffTest do defstruct [:name, :age] end + defmodule Customer do + defstruct [:address, :age, :first_name, :language, :last_name, :notifications] + end + defmodule Person do defstruct [:age] end @@ -875,17 +879,17 @@ defmodule ExUnit.DiffTest do test "maps in lists" do map = %{ - first_name: "John", - last_name: "Doe", - age: 30, - notifications: true, - language: "en-US", address: %{ street: "123 Main St", city: "Springfield", state: "IL", zip: "62701" - } + }, + age: 30, + first_name: "John", + language: "en-US", + last_name: "Doe", + notifications: true } refute_diff( @@ -893,12 +897,12 @@ defmodule ExUnit.DiffTest do """ [ -%{ - address: %{state: \"IL\", zip: \"62701\", street: \"123 Main St\", city: \"Springfield\"}, + address: %{state: "IL", zip: "62701", street: "123 Main St", city: "Springfield"}, age: 30, - first_name: \"John\", - last_name: \"Doe\", - notifications: true, - language: \"en-US\" + first_name: "John", + language: "en-US", + last_name: "Doe", + notifications: true }- ]\ """, @@ -911,12 +915,12 @@ defmodule ExUnit.DiffTest do """ [ +%{ - address: %{state: \"IL\", zip: \"62701\", street: \"123 Main St\", city: \"Springfield\"}, + address: %{state: "IL", zip: "62701", street: "123 Main St", city: "Springfield"}, age: 30, - first_name: \"John\", - last_name: \"Doe\", - notifications: true, - language: \"en-US\" + first_name: "John", + language: "en-US", + last_name: "Doe", + notifications: true }+ ]\ """ @@ -925,6 +929,58 @@ defmodule ExUnit.DiffTest do assert_diff([map] == [map], []) end + test "structs in lists" do + customer = %Customer{ + address: %{ + street: "123 Main St", + city: "Springfield", + state: "IL", + zip: "62701" + }, + age: 30, + first_name: "John", + language: "en-US", + last_name: "Doe", + notifications: true + } + + refute_diff( + [customer] == [], + """ + [ + -%ExUnit.DiffTest.Customer{ + address: %{state: "IL", zip: "62701", street: "123 Main St", city: "Springfield"}, + age: 30, + first_name: "John", + language: "en-US", + last_name: "Doe", + notifications: true + }- + ]\ + """, + "[]" + ) + + refute_diff( + [] == [customer], + "[]", + """ + [ + +%ExUnit.DiffTest.Customer{ + address: %{state: "IL", zip: "62701", street: "123 Main St", city: "Springfield"}, + age: 30, + first_name: "John", + language: "en-US", + last_name: "Doe", + notifications: true + }+ + ]\ + """ + ) + + assert_diff([customer] == [customer], []) + end + test "maps and structs with escaped values" do refute_diff( %User{age: {1, 2, 3}} = %User{age: {1, 2, 4}},