diff --git a/lib/ex_unit/lib/ex_unit/diff.ex b/lib/ex_unit/lib/ex_unit/diff.ex index 05c2c7fda7e..f784d1a60b4 100644 --- a/lib/ex_unit/lib/ex_unit/diff.ex +++ b/lib/ex_unit/lib/ex_unit/diff.ex @@ -1142,9 +1142,22 @@ 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. + # We escape container types to make a distinction between AST and values that + # 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_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 04340b8db5a..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 @@ -126,10 +130,11 @@ defmodule ExUnit.DiffTest do assert env_binding == expected_binding end + @terminal_width 80 defp to_diff(side, sign) do side |> Diff.to_algebra(&diff_wrapper(&1, sign)) - |> Algebra.format(:infinity) + |> Algebra.format(@terminal_width) |> IO.iodata_to_binary() end @@ -682,13 +687,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 +877,110 @@ defmodule ExUnit.DiffTest do ) end + test "maps in lists" do + map = %{ + 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( + [map] == [], + """ + [ + -%{ + 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( + [] == [map], + "[]", + """ + [ + +%{ + 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([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}}, @@ -1082,8 +1227,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( @@ -1100,7 +1255,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, @@ -1141,7 +1305,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),