Skip to content

Commit 9cdff30

Browse files
authored
Add :sort_maps to Inspect.Opts.custom_options (#12404)
1 parent 7f2c927 commit 9cdff30

File tree

4 files changed

+34
-13
lines changed

4 files changed

+34
-13
lines changed

lib/elixir/lib/inspect.ex

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,12 @@ end
302302

303303
defimpl Inspect, for: Map do
304304
def inspect(map, opts) do
305-
list = Map.to_list(map)
305+
list =
306+
if Keyword.get(opts.custom_options, :sort_maps) do
307+
map |> Map.to_list() |> :lists.sort()
308+
else
309+
Map.to_list(map)
310+
end
306311

307312
fun =
308313
if Inspect.List.keyword?(list) do

lib/elixir/lib/inspect/algebra.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ defmodule Inspect.Opts do
3131
options. Useful when implementing the `Inspect` protocol for nested structs
3232
to pass the custom options through.
3333
34+
It supports some pre-defined keys:
35+
- `:sort_maps` (since v1.15.0) - if set to `true`, sorts key-value pairs in maps.
36+
This can be helpful to make map inspection deterministic for testing,
37+
especially since key order is random since OTP 26.
38+
3439
* `:inspect_fun` (since v1.9.0) - a function to build algebra documents.
3540
Defaults to `Inspect.Opts.default_inspect_fun/0`.
3641

lib/elixir/test/elixir/inspect_test.exs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -417,14 +417,20 @@ defmodule Inspect.MapTest do
417417
test "basic" do
418418
assert inspect(%{1 => "b"}) == "%{1 => \"b\"}"
419419

420-
assert inspect(%{1 => "b", 2 => "c"}, pretty: true, width: 1) ==
420+
assert inspect(%{1 => "b", 2 => "c"},
421+
pretty: true,
422+
width: 1,
423+
custom_options: [sort_maps: true]
424+
) ==
421425
"%{\n 1 => \"b\",\n 2 => \"c\"\n}"
422426
end
423427

424428
test "keyword" do
425429
assert inspect(%{a: 1}) == "%{a: 1}"
426-
assert inspect(%{a: 1, b: 2}) == "%{a: 1, b: 2}"
427-
assert inspect(%{a: 1, b: 2, c: 3}) == "%{a: 1, b: 2, c: 3}"
430+
assert inspect(%{a: 1, b: 2}, custom_options: [sort_maps: true]) == "%{a: 1, b: 2}"
431+
432+
assert inspect(%{a: 1, b: 2, c: 3}, custom_options: [sort_maps: true]) ==
433+
"%{a: 1, b: 2, c: 3}"
428434
end
429435

430436
test "with limit" do
@@ -446,12 +452,12 @@ defmodule Inspect.MapTest do
446452
test "public modified struct" do
447453
public = %Public{key: 1}
448454

449-
assert inspect(Map.put(public, :foo, :bar)) ==
455+
assert inspect(Map.put(public, :foo, :bar), custom_options: [sort_maps: true]) ==
450456
"%{__struct__: Inspect.MapTest.Public, foo: :bar, key: 1}"
451457
end
452458

453459
test "private struct" do
454-
assert inspect(%{__struct__: Private, key: 1}) ==
460+
assert inspect(%{__struct__: Private, key: 1}, custom_options: [sort_maps: true]) ==
455461
"%{__struct__: Inspect.MapTest.Private, key: 1}"
456462
end
457463

@@ -486,7 +492,7 @@ defmodule Inspect.MapTest do
486492
%{__struct__: Inspect.MapTest.Failing, name: "Foo"}
487493
'''
488494

489-
assert inspect(%Failing{name: "Foo"}) =~ message
495+
assert inspect(%Failing{name: "Foo"}, custom_options: [sort_maps: true]) =~ message
490496
end
491497

492498
test "safely inspect bad implementation disables colors" do
@@ -505,7 +511,10 @@ defmodule Inspect.MapTest do
505511
%{__struct__: Inspect.MapTest.Failing, name: "Foo"}
506512
'''
507513

508-
assert inspect(%Failing{name: "Foo"}, syntax_colors: [atom: [:green]]) =~ message
514+
assert inspect(%Failing{name: "Foo"},
515+
syntax_colors: [atom: [:green]],
516+
custom_options: [sort_maps: true]
517+
) =~ message
509518
end
510519

511520
test "unsafely inspect bad implementation" do
@@ -524,7 +533,7 @@ defmodule Inspect.MapTest do
524533
'''
525534

526535
try do
527-
inspect(%Failing{name: "Foo"}, safe: false)
536+
inspect(%Failing{name: "Foo"}, safe: false, custom_options: [sort_maps: true])
528537
rescue
529538
exception in Inspect.Error ->
530539
assert Exception.message(exception) =~ exception_message
@@ -552,14 +561,16 @@ defmodule Inspect.MapTest do
552561
553562
while inspecting:
554563
555-
%{__struct__: Inspect.MapTest.Failing, name: "Foo"}
556564
'''
557565

558566
try do
559567
Enum.to_list(%Failing{name: "Foo"})
560568
rescue
561569
exception in Protocol.UndefinedError ->
562-
assert Exception.message(exception) =~ exception_message
570+
message = Exception.message(exception)
571+
assert message =~ exception_message
572+
assert message =~ "__struct__: Inspect.MapTest.Failing"
573+
assert message =~ "name: \"Foo\""
563574

564575
assert [
565576
{Enumerable, :impl_for!, 1, _} | _
@@ -617,7 +628,7 @@ defmodule Inspect.MapTest do
617628
)
618629
)
619630

620-
assert inspect(%Failing{name: "Foo"}) =~ message
631+
assert inspect(%Failing{name: "Foo"}, custom_options: [sort_maps: true]) =~ message
621632
assert inspected =~ message
622633
end
623634

lib/elixir/test/elixir/map_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ defmodule MapTest do
222222
assert Map.replace!(map, :b, 10) == %{c: 3, b: 10, a: 1}
223223
assert Map.replace!(map, :a, 1) == map
224224

225-
assert_raise KeyError, "key :x not found in: %{a: 1, b: 2, c: 3}", fn ->
225+
assert_raise KeyError, ~r/key :x not found in: %{.*a: 1.*}/, fn ->
226226
Map.replace!(map, :x, 10)
227227
end
228228

0 commit comments

Comments
 (0)