Skip to content

Use Test Modules in tests instead of Elixir built-in modules #14383

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 2 commits into from
Apr 7, 2025
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
41 changes: 31 additions & 10 deletions lib/elixir/test/elixir/exception_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -556,20 +556,33 @@ defmodule ExceptionTest do
end

test "annotates function clause errors" do
assert blame_message(Access, & &1.fetch(:foo, :bar)) =~ """
no function clause matching in Access.fetch/2
import PathHelpers

write_beam(
defmodule ExampleModule do
def fun(arg1, arg2)
def fun(:one, :one), do: :ok
def fun(:two, :two), do: :ok
end
)

message = blame_message(ExceptionTest.ExampleModule, & &1.fun(:three, :four))

assert message =~ """
no function clause matching in ExceptionTest.ExampleModule.fun/2

The following arguments were given to Access.fetch/2:
The following arguments were given to ExceptionTest.ExampleModule.fun/2:

# 1
:foo
:three

# 2
:bar
:four

Attempted function clauses (showing 5 out of 5):
Attempted function clauses (showing 2 out of 2):

def fetch(-%module{} = container-, key)
def fun(-:one-, -:one-)
def fun(-:two-, -:two-)
"""
end

Expand Down Expand Up @@ -858,13 +871,21 @@ defmodule ExceptionTest do

describe "blaming unit tests" do
test "annotates clauses errors" do
args = [%{}, :key, nil]
import PathHelpers

write_beam(
defmodule BlameModule do
def fun(arg), do: arg
end
)

args = [nil]

{exception, stack} =
Exception.blame(:error, :function_clause, [{Keyword, :pop, args, [line: 13]}])
Exception.blame(:error, :function_clause, [{BlameModule, :fun, args, [line: 13]}])

assert %FunctionClauseError{kind: :def, args: ^args, clauses: [_]} = exception
assert stack == [{Keyword, :pop, 3, [line: 13]}]
assert stack == [{BlameModule, :fun, 1, [line: 13]}]
end

test "annotates args and clauses from mfa" do
Expand Down
13 changes: 11 additions & 2 deletions lib/elixir/test/elixir/module_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -432,9 +432,18 @@ defmodule ModuleTest do
end

test "compiles to core" do
{:ok, {Atom, [{~c"Dbgi", dbgi}]}} = Atom |> :code.which() |> :beam_lib.chunks([~c"Dbgi"])
import PathHelpers

write_beam(
defmodule ExampleModule do
end
)

{:ok, {ExampleModule, [{~c"Dbgi", dbgi}]}} =
ExampleModule |> :code.which() |> :beam_lib.chunks([~c"Dbgi"])

{:debug_info_v1, backend, data} = :erlang.binary_to_term(dbgi)
{:ok, core} = backend.debug_info(:core_v1, Atom, data, [])
{:ok, core} = backend.debug_info(:core_v1, ExampleModule, data, [])
assert is_tuple(core)
end

Expand Down
9 changes: 8 additions & 1 deletion lib/elixir/test/elixir/protocol/consolidation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,18 @@ defmodule Protocol.ConsolidationTest do
end

test "consolidation errors on missing BEAM files" do
import PathHelpers

write_beam(
defmodule ExampleModule do
end
)

defprotocol NoBeam do
def example(arg)
end

assert Protocol.consolidate(String, []) == {:error, :not_a_protocol}
assert Protocol.consolidate(ExampleModule, []) == {:error, :not_a_protocol}
assert Protocol.consolidate(NoBeam, []) == {:error, :no_beam_info}
end

Expand Down
4 changes: 4 additions & 0 deletions lib/elixir/test/elixir/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ defmodule PathHelpers do
File.mkdir_p!(unquote(path))
beam_path = Path.join(unquote(path), Atom.to_string(name) <> ".beam")
File.write!(beam_path, bin)

:code.purge(name)
:code.delete(name)

res
end

Expand Down
91 changes: 49 additions & 42 deletions lib/iex/test/iex/helpers_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,53 +39,53 @@ defmodule IEx.HelpersTest do
end

test "sets up a breakpoint with capture syntax" do
assert break!(URI.decode_query() / 2) == 1
assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}]
assert break!(PryExampleModule.two() / 2) == 1
assert IEx.Pry.breaks() == [{1, PryExampleModule, {:two, 2}, 1}]
end

test "sets up a breakpoint with call syntax" do
assert break!(URI.decode_query(_, %{})) == 1
assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}]
assert break!(PryExampleModule.two(_, %{})) == 1
assert IEx.Pry.breaks() == [{1, PryExampleModule, {:two, 2}, 1}]
end

test "sets up a breakpoint with guards syntax" do
assert break!(URI.decode_query(_, map) when is_map(map)) == 1
assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}]
assert break!(PryExampleModule.two(_, map) when is_map(map)) == 1
assert IEx.Pry.breaks() == [{1, PryExampleModule, {:two, 2}, 1}]
end

test "sets up a breakpoint on the given module" do
assert break!(URI, :decode_query, 2) == 1
assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}]
assert break!(PryExampleModule, :two, 2) == 1
assert IEx.Pry.breaks() == [{1, PryExampleModule, {:two, 2}, 1}]
end

test "resets breaks on the given ID" do
assert break!(URI, :decode_query, 2) == 1
assert break!(PryExampleModule, :two, 2) == 1
assert reset_break(1) == :ok
assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}]
assert IEx.Pry.breaks() == [{1, PryExampleModule, {:two, 2}, 0}]
end

test "resets breaks on the given module" do
assert break!(URI, :decode_query, 2) == 1
assert reset_break(URI, :decode_query, 2) == :ok
assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}]
assert break!(PryExampleModule, :two, 2) == 1
assert reset_break(PryExampleModule, :two, 2) == :ok
assert IEx.Pry.breaks() == [{1, PryExampleModule, {:two, 2}, 0}]
end

test "removes breaks in the given module" do
assert break!(URI.decode_query() / 2) == 1
assert remove_breaks(URI) == :ok
assert break!(PryExampleModule.two() / 2) == 1
assert remove_breaks(PryExampleModule) == :ok
assert IEx.Pry.breaks() == []
end

test "removes breaks on all modules" do
assert break!(URI.decode_query() / 2) == 1
assert break!(PryExampleModule.two() / 2) == 1
assert remove_breaks() == :ok
assert IEx.Pry.breaks() == []
end

test "errors when setting up a breakpoint with invalid guard" do
assert capture_io(:stderr, fn ->
assert_raise CompileError, fn ->
break!(URI.decode_query(_, map) when is_whatever(map))
break!(PryExampleModule.two(_, map) when is_whatever(map))
end
end) =~ "cannot find or invoke local is_whatever/1"
end
Expand All @@ -98,44 +98,44 @@ defmodule IEx.HelpersTest do

test "errors when setting up a break for unknown function" do
assert_raise RuntimeError,
"could not set breakpoint, unknown function/macro URI.unknown/2",
fn -> break!(URI, :unknown, 2) end
"could not set breakpoint, unknown function/macro #{inspect(PryExampleModule)}.unknown/2",
fn -> break!(PryExampleModule, :unknown, 2) end
end

test "errors for non-Elixir modules" do
assert_raise RuntimeError,
"could not set breakpoint, module :elixir was not written in Elixir",
fn -> break!(:elixir, :unknown, 2) end
"could not set breakpoint, module :maps was not written in Elixir",
fn -> break!(:maps, :unknown, 2) end
end

test "prints table with breaks" do
break!(URI, :decode_query, 2)
break!(PryExampleModule, :two, 2)

assert capture_io(fn -> breaks() end) == """

ID Module.function/arity Pending stops
---- ----------------------- ---------------
1 URI.decode_query/2 1
ID Module.function/arity Pending stops
---- ------------------------ ---------------
1 PryExampleModule.two/2 1

"""

assert capture_io(fn -> URI.decode_query("foo=bar", %{}) end) != ""
assert capture_io(fn -> PryExampleModule.two("foo=bar", %{}) end) != ""

assert capture_io(fn -> breaks() end) == """

ID Module.function/arity Pending stops
---- ----------------------- ---------------
1 URI.decode_query/2 0
ID Module.function/arity Pending stops
---- ------------------------ ---------------
1 PryExampleModule.two/2 0

"""

assert capture_io(fn -> URI.decode_query("foo=bar", %{}) end) == ""
assert capture_io(fn -> PryExampleModule.two("foo=bar", %{}) end) == ""

assert capture_io(fn -> breaks() end) == """

ID Module.function/arity Pending stops
---- ----------------------- ---------------
1 URI.decode_query/2 0
ID Module.function/arity Pending stops
---- ------------------------ ---------------
1 PryExampleModule.two/2 0

"""
end
Expand All @@ -152,6 +152,7 @@ defmodule IEx.HelpersTest do
@lists_erl Application.app_dir(:stdlib, "src/lists.erl")
@httpc_erl "src/http_client/httpc.erl"
@editor System.get_env("ELIXIR_EDITOR")
@example_module_path "lib/iex/test/test_helper.exs"

test "opens __FILE__ and __LINE__" do
System.put_env("ELIXIR_EDITOR", "echo __LINE__:__FILE__")
Expand All @@ -163,7 +164,8 @@ defmodule IEx.HelpersTest do
end

test "opens Elixir module" do
assert capture_iex("open(IEx.Helpers)") |> maybe_trim_quotes() =~ ~r/#{@iex_helpers}:5$/
assert capture_iex("open(HelperExampleModule)") |> maybe_trim_quotes() =~
~r/#{@example_module_path}:\d+$/
end

test "opens function" do
Expand All @@ -176,16 +178,19 @@ defmodule IEx.HelpersTest do
end

test "opens module.function" do
assert capture_iex("open(IEx.Helpers.b)") |> maybe_trim_quotes() =~ ~r/#{@iex_helpers}:\d+$/
assert capture_iex("open(IEx.Helpers.h)") |> maybe_trim_quotes() =~ ~r/#{@iex_helpers}:\d+$/
assert capture_iex("open(HelperExampleModule.fun)") |> maybe_trim_quotes() =~
~r/#{@example_module_path}:\d+$/

assert capture_iex("open(HelperExampleModule.macro)") |> maybe_trim_quotes() =~
~r/#{@example_module_path}:\d+$/
end

test "opens module.function/arity" do
assert capture_iex("open(IEx.Helpers.b/1)") |> maybe_trim_quotes() =~
~r/#{@iex_helpers}:\d+$/
assert capture_iex("open(HelperExampleModule.fun/1)") |> maybe_trim_quotes() =~
~r/#{@example_module_path}:\d+$/

assert capture_iex("open(IEx.Helpers.h/0)") |> maybe_trim_quotes() =~
~r/#{@iex_helpers}:\d+$/
assert capture_iex("open(HelperExampleModule.macro/1)") |> maybe_trim_quotes() =~
~r/#{@example_module_path}:\d+$/
end

test "opens Erlang module" do
Expand Down Expand Up @@ -1440,11 +1445,13 @@ defmodule IEx.HelpersTest do
@tag :capture_log
test "loads a given module on the given nodes" do
assert nl([node()], :lists) == {:ok, [{:nonode@nohost, :error, :sticky_directory}]}
assert nl([node()], Enum) == {:ok, [{:nonode@nohost, :loaded, Enum}]}

assert nl([node()], HelperExampleModule) ==
{:ok, [{:nonode@nohost, :loaded, HelperExampleModule}]}

assert nl(:nonexistent_module) == {:error, :nofile}

assert nl([:nosuchnode@badhost], Enum) ==
assert nl([:nosuchnode@badhost], HelperExampleModule) ==
{:ok, [{:nosuchnode@badhost, :badrpc, :noconnection}]}
end
end
Expand Down
27 changes: 23 additions & 4 deletions lib/iex/test/iex/interaction_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,30 @@ defmodule IEx.InteractionTest do
end

test "blames function clause error" do
import PathHelpers

write_beam(
defmodule ExampleModule do
def fun(:valid), do: :ok
end
)

content = capture_iex("IEx.InteractionTest.ExampleModule.fun(:invalid)")

assert content =~
"** (FunctionClauseError) no function clause matching in IEx.InteractionTest.ExampleModule.fun/1"

assert content =~
"The following arguments were given to IEx.InteractionTest.ExampleModule.fun/1"

assert content =~ ":invalid"
assert content =~ "def fun(-:valid-)"

assert content =~
~r"test/iex/interaction_test.exs:\d+: IEx\.InteractionTest\.ExampleModule\.fun/1"

content = capture_iex("Access.fetch(:foo, :bar)")
assert content =~ "** (FunctionClauseError) no function clause matching in Access.fetch/2"
assert content =~ "The following arguments were given to Access.fetch/2"
assert content =~ ":foo"
assert content =~ "def fetch(-%module{} = container-, key)"

assert content =~ ~r"\(elixir #{System.version()}\) lib/access\.ex:\d+: Access\.fetch/2"
end

Expand Down
Loading
Loading