Skip to content

Fix Enum tests for maps in OTP26 #12405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2302,13 +2302,13 @@ defmodule Enum do
iex> Enum.split_with([5, 4, 3, 2, 1, 0], fn x -> rem(x, 2) == 0 end)
{[4, 2, 0], [5, 3, 1]}

iex> Enum.split_with(%{a: 1, b: -2, c: 1, d: -3}, fn {_k, v} -> v < 0 end)
iex> Enum.split_with([a: 1, b: -2, c: 1, d: -3], fn {_k, v} -> v < 0 end)
{[b: -2, d: -3], [a: 1, c: 1]}

iex> Enum.split_with(%{a: 1, b: -2, c: 1, d: -3}, fn {_k, v} -> v > 50 end)
iex> Enum.split_with([a: 1, b: -2, c: 1, d: -3], fn {_k, v} -> v > 50 end)
{[], [a: 1, b: -2, c: 1, d: -3]}

iex> Enum.split_with(%{}, fn {_k, v} -> v > 50 end)
iex> Enum.split_with([], fn {_k, v} -> v > 50 end)
{[], []}

"""
Expand Down
125 changes: 61 additions & 64 deletions lib/elixir/test/elixir/enum_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -735,11 +735,11 @@ defmodule EnumTest do

assert Enum.split_with(%{}, fn x -> rem(x, 2) == 0 end) == {[], []}

assert Enum.split_with(%{a: 1, b: 2, c: 3}, fn {_k, v} -> rem(v, 2) == 0 end) ==
{[b: 2], [a: 1, c: 3]}
assert Enum.split_with(%{a: 1, b: 2}, fn {_k, v} -> rem(v, 2) == 0 end) ==
{[b: 2], [a: 1]}

assert Enum.split_with(%{b: 2, d: 4, f: 6}, fn {_k, v} -> rem(v, 2) == 0 end) ==
{[b: 2, d: 4, f: 6], []}
{Map.to_list(%{b: 2, d: 4, f: 6}), []}
end

test "random/1" do
Expand Down Expand Up @@ -1503,23 +1503,22 @@ defmodule EnumTest do
assert result == [2, 4, 6, 8, 10]

# Maps
result = Enum.zip_with(%{a: 7, c: 9}, 3..4, fn {key, value}, b -> {key, value + b} end)
assert result == [a: 10, c: 13]
result = Enum.zip_with(%{a: 7}, 3..4, fn {key, value}, b -> {key, value + b} end)
assert result == [a: 10]

colour_1 = %{r: 176, g: 175, b: 255}
colour_2 = %{r: 12, g: 176, b: 176}

result = Enum.zip_with(colour_1, colour_2, fn {k, left}, {k, right} -> {k, left + right} end)
assert result == [b: 431, g: 351, r: 188]
result = Enum.zip_with(3..4, %{a: 7}, fn a, {key, value} -> {key, value + a} end)
assert result == [a: 10]
end

test "zip_with/2" do
zip_fun = fn items -> List.to_tuple(items) end
result = Enum.zip_with([[:a, :b], [1, 2], ["foo", "bar"]], zip_fun)
assert result == [{:a, 1, "foo"}, {:b, 2, "bar"}]

lots = Enum.zip_with([[:a, :b], [1, 2], ["foo", "bar"], %{a: :b, c: :d}], zip_fun)
assert lots == [{:a, 1, "foo", {:a, :b}}, {:b, 2, "bar", {:c, :d}}]
map = %{a: :b, c: :d}
[x1, x2] = Map.to_list(map)
lots = Enum.zip_with([[:a, :b], [1, 2], ["foo", "bar"], map], zip_fun)
assert lots == [{:a, 1, "foo", x1}, {:b, 2, "bar", x2}]

assert Enum.zip_with([[:a, :b], [1, 2, 3, 4], ["foo", "bar", "baz", "qux"]], zip_fun) ==
[{:a, 1, "foo"}, {:b, 2, "bar"}]
Expand Down Expand Up @@ -1556,21 +1555,11 @@ defmodule EnumTest do
assert result == [2, 4, 6, 8, 10]

# Maps
result = Enum.zip_with([%{a: 7, c: 9}, 3..4], fn [{key, value}, b] -> {key, value + b} end)
assert result == [a: 10, c: 13]

colour_1 = %{r: 176, g: 175, b: 255}
colour_2 = %{r: 12, g: 176, b: 176}

result =
Enum.zip_with([colour_1, colour_2], fn [{k, left}, {k, right}] -> {k, left + right} end)

assert result == [b: 431, g: 351, r: 188]
result = Enum.zip_with([%{a: 7}, 3..4], fn [{key, value}, b] -> {key, value + b} end)
assert result == [a: 10]

assert Enum.zip_with([%{a: :b, c: :d}, %{e: :f, g: :h}], & &1) == [
[a: :b, e: :f],
[c: :d, g: :h]
]
result = Enum.zip_with([%{a: 7}, 3..4], fn [{key, value}, b] -> {key, value + b} end)
assert result == [a: 10]
end
end

Expand Down Expand Up @@ -2396,16 +2385,17 @@ defmodule EnumTest.Map do

test "random/1" do
map = %{a: 1, b: 2, c: 3}
[x1, x2, x3] = Map.to_list(map)
seed1 = {1406, 407_414, 139_258}
seed2 = {1406, 421_106, 567_597}
:rand.seed(:exsss, seed1)
assert Enum.random(map) == {:c, 3}
assert Enum.random(map) == {:a, 1}
assert Enum.random(map) == {:b, 2}
assert Enum.random(map) == x3
assert Enum.random(map) == x1
assert Enum.random(map) == x2

:rand.seed(:exsss, seed2)
assert Enum.random(map) == {:c, 3}
assert Enum.random(map) == {:b, 2}
assert Enum.random(map) == x3
assert Enum.random(map) == x2
end

test "take_random/2" do
Expand All @@ -2418,35 +2408,37 @@ defmodule EnumTest.Map do
# set a fixed seed so the test can be deterministic
# please note the order of following assertions is important
map = %{a: 1, b: 2, c: 3}
[x1, x2, x3] = Map.to_list(map)
seed1 = {1406, 407_414, 139_258}
seed2 = {1406, 421_106, 567_597}
:rand.seed(:exsss, seed1)
assert Enum.take_random(map, 1) == [c: 3]
assert Enum.take_random(map, 1) == [x3]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took this approache here, but I'm not sure if it's the best here. Maybe these tests could actually be removed and more property-based testing could be added in StreamData?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are silly in a way but important because we often have special clauses for lists, so we need to test the enum implementations. The other option is to use MapSet, but they don't guarantee order as well, so...

If we had a queue :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see!
If we want something that's a) ordered and b) not optimized (rules out ranges), I think streams could be a nice candidate? Something like:
https://github.com/elixir-lang/elixir/compare/main...sabiwara:elixir:use-stream-for-enum-tests?expand=1

If there's interest, I can open up a PR.

Noted for the queue ;) (but this might be optimizeable too)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, streams are an option and we have tests with them too. But I would only add tests, the changes you did are great.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically keep EnumTest.Map but add EnumTest.Stream as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If necessary, surely, but i assume we are well covered? :)

:rand.seed(:exsss, seed1)
assert Enum.take_random(map, 2) == [c: 3, a: 1]
assert Enum.take_random(map, 2) == [x3, x1]
:rand.seed(:exsss, seed1)
assert Enum.take_random(map, 3) == [c: 3, a: 1, b: 2]
assert Enum.take_random(map, 3) == [x3, x1, x2]
:rand.seed(:exsss, seed1)
assert Enum.take_random(map, 4) == [c: 3, a: 1, b: 2]
assert Enum.take_random(map, 4) == [x3, x1, x2]
:rand.seed(:exsss, seed2)
assert Enum.take_random(map, 1) == [a: 1]
assert Enum.take_random(map, 1) == [x1]
:rand.seed(:exsss, seed2)
assert Enum.take_random(map, 2) == [a: 1, c: 3]
assert Enum.take_random(map, 2) == [x1, x3]
:rand.seed(:exsss, seed2)
assert Enum.take_random(map, 3) == [a: 1, c: 3, b: 2]
assert Enum.take_random(map, 3) == [x1, x3, x2]
:rand.seed(:exsss, seed2)
assert Enum.take_random(map, 4) == [a: 1, c: 3, b: 2]
assert Enum.take_random(map, 4) == [x1, x3, x2]
end

test "reverse/1" do
assert Enum.reverse(%{}) == []
assert Enum.reverse(MapSet.new()) == []
assert Enum.reverse(%{a: 1, b: 2, c: 3}) == [c: 3, b: 2, a: 1]

map = %{a: 1, b: 2, c: 3}
assert Enum.reverse(map) == Map.to_list(map) |> Enum.reverse()
end

test "reverse/2" do
assert Enum.reverse([a: 1, b: 2, c: 3, a: 1], %{x: 1, y: 2, z: 3}) ==
[a: 1, c: 3, b: 2, a: 1, x: 1, y: 2, z: 3]
assert Enum.reverse([a: 1, b: 2, c: 3, a: 1], %{x: 1}) == [a: 1, c: 3, b: 2, a: 1, x: 1]

assert Enum.reverse([], %{a: 1}) == [a: 1]
assert Enum.reverse([], %{}) == []
Expand All @@ -2456,44 +2448,49 @@ defmodule EnumTest.Map do

test "fetch/2" do
map = %{a: 1, b: 2, c: 3, d: 4, e: 5}
assert Enum.fetch(map, 0) == {:ok, {:a, 1}}
assert Enum.fetch(map, -2) == {:ok, {:d, 4}}
[x1, _x2, _x3, x4, x5] = Map.to_list(map)
assert Enum.fetch(map, 0) == {:ok, x1}
assert Enum.fetch(map, -2) == {:ok, x4}
assert Enum.fetch(map, -6) == :error
assert Enum.fetch(map, 5) == :error
assert Enum.fetch(%{}, 0) == :error

assert Stream.take(map, 3) |> Enum.fetch(3) == :error
assert Stream.take(map, 5) |> Enum.fetch(4) == {:ok, {:e, 5}}
assert Stream.take(map, 5) |> Enum.fetch(4) == {:ok, x5}
end

test "map_intersperse/3" do
assert Enum.map_intersperse(%{}, :a, & &1) == []
assert Enum.map_intersperse(%{foo: :bar}, :a, & &1) == [{:foo, :bar}]

assert Enum.map_intersperse(%{foo: :bar, baz: :bat}, :a, & &1) ==
[{:baz, :bat}, :a, {:foo, :bar}]
map = %{foo: :bar, baz: :bat}
[x1, x2] = Map.to_list(map)

assert Enum.map_intersperse(map, :a, & &1) == [x1, :a, x2]
end

test "slice/2" do
map = %{a: 1, b: 2, c: 3, d: 4, e: 5}
assert Enum.slice(map, 0..0) == [a: 1]
assert Enum.slice(map, 0..1) == [a: 1, b: 2]
assert Enum.slice(map, 0..2) == [a: 1, b: 2, c: 3]
[x1, x2, x3 | _] = Map.to_list(map)
assert Enum.slice(map, 0..0) == [x1]
assert Enum.slice(map, 0..1) == [x1, x2]
assert Enum.slice(map, 0..2) == [x1, x2, x3]
end

test "slice/3" do
map = %{a: 1, b: 2, c: 3, d: 4, e: 5}
assert Enum.slice(map, 1, 2) == [b: 2, c: 3]
[x1, x2, x3, x4, x5] = Map.to_list(map)
assert Enum.slice(map, 1, 2) == [x2, x3]
assert Enum.slice(map, 1, 0) == []
assert Enum.slice(map, 2, 5) == [c: 3, d: 4, e: 5]
assert Enum.slice(map, 2, 6) == [c: 3, d: 4, e: 5]
assert Enum.slice(map, 2, 5) == [x3, x4, x5]
assert Enum.slice(map, 2, 6) == [x3, x4, x5]
assert Enum.slice(map, 5, 5) == []
assert Enum.slice(map, 6, 5) == []
assert Enum.slice(map, 6, 0) == []
assert Enum.slice(map, -6, 0) == []
assert Enum.slice(map, -6, 5) == [a: 1, b: 2, c: 3, d: 4, e: 5]
assert Enum.slice(map, -2, 5) == [d: 4, e: 5]
assert Enum.slice(map, -3, 1) == [c: 3]
assert Enum.slice(map, -6, 5) == [x1, x2, x3, x4, x5]
assert Enum.slice(map, -2, 5) == [x4, x5]
assert Enum.slice(map, -3, 1) == [x3]

assert_raise FunctionClauseError, fn ->
Enum.slice(map, 0, -1)
Expand All @@ -2508,19 +2505,19 @@ defmodule EnumTest.Map do
end

assert Enum.slice(map, 0, 0) == []
assert Enum.slice(map, 0, 1) == [a: 1]
assert Enum.slice(map, 0, 2) == [a: 1, b: 2]
assert Enum.slice(map, 1, 2) == [b: 2, c: 3]
assert Enum.slice(map, 0, 1) == [x1]
assert Enum.slice(map, 0, 2) == [x1, x2]
assert Enum.slice(map, 1, 2) == [x2, x3]
assert Enum.slice(map, 1, 0) == []
assert Enum.slice(map, 2, 5) == [c: 3, d: 4, e: 5]
assert Enum.slice(map, 2, 6) == [c: 3, d: 4, e: 5]
assert Enum.slice(map, 2, 5) == [x3, x4, x5]
assert Enum.slice(map, 2, 6) == [x3, x4, x5]
assert Enum.slice(map, 5, 5) == []
assert Enum.slice(map, 6, 5) == []
assert Enum.slice(map, 6, 0) == []
assert Enum.slice(map, -6, 0) == []
assert Enum.slice(map, -6, 5) == [a: 1, b: 2, c: 3, d: 4, e: 5]
assert Enum.slice(map, -2, 5) == [d: 4, e: 5]
assert Enum.slice(map, -3, 1) == [c: 3]
assert Enum.slice(map, -6, 5) == [x1, x2, x3, x4, x5]
assert Enum.slice(map, -2, 5) == [x4, x5]
assert Enum.slice(map, -3, 1) == [x3]

assert_raise FunctionClauseError, fn ->
Enum.slice(map, 0, -1)
Expand Down