diff options
author | José Valim <jose.valim@gmail.com> | 2018-11-17 17:13:11 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-17 17:13:11 +0100 |
commit | d1e3e4dafd48e55898ec62235d6f91ab5aa7bc64 (patch) | |
tree | e1bb9a8e15623ec9f4ede109a18c0dd0656443cd | |
parent | e1ee8d134e74966dac59fd6deada1c97b92d0e71 (diff) | |
download | elixir-d1e3e4dafd48e55898ec62235d6f91ab5aa7bc64.tar.gz |
Introduce IEx.Server.run/1 (#8395)
Since now there may be multiple servers running,
we also introduced IEx.Broker to negociate pry
requests across multiple sessions.
Shell sessions though are tracked separately from
the remaining ones, as we delegate the shell control
to Erlang's user_drv.
-rw-r--r-- | lib/iex/lib/iex.ex | 2 | ||||
-rw-r--r-- | lib/iex/lib/iex/app.ex | 2 | ||||
-rw-r--r-- | lib/iex/lib/iex/autocomplete.ex | 2 | ||||
-rw-r--r-- | lib/iex/lib/iex/broker.ex | 181 | ||||
-rw-r--r-- | lib/iex/lib/iex/evaluator.ex | 9 | ||||
-rw-r--r-- | lib/iex/lib/iex/helpers.ex | 8 | ||||
-rw-r--r-- | lib/iex/lib/iex/pry.ex | 6 | ||||
-rw-r--r-- | lib/iex/lib/iex/server.ex | 155 | ||||
-rw-r--r-- | lib/iex/test/iex/interaction_test.exs | 2 | ||||
-rw-r--r-- | lib/iex/test/iex/server_test.exs | 168 | ||||
-rw-r--r-- | lib/iex/test/test_helper.exs | 4 |
11 files changed, 390 insertions, 149 deletions
diff --git a/lib/iex/lib/iex.ex b/lib/iex/lib/iex.ex index f648f2f1e..e0e1e82d3 100644 --- a/lib/iex/lib/iex.ex +++ b/lib/iex/lib/iex.ex @@ -773,7 +773,7 @@ defmodule IEx do :ok = start_iex() :ok = set_expand_fun() :ok = run_after_spawn() - IEx.Server.start(opts, mfa) + IEx.Server.run_from_shell(opts, mfa) end) end diff --git a/lib/iex/lib/iex/app.ex b/lib/iex/lib/iex/app.ex index 1c2f55adb..662626e53 100644 --- a/lib/iex/lib/iex/app.ex +++ b/lib/iex/lib/iex/app.ex @@ -4,7 +4,7 @@ defmodule IEx.App do use Application def start(_type, _args) do - children = [IEx.Config, IEx.Pry] + children = [IEx.Config, IEx.Broker, IEx.Pry] Supervisor.start_link(children, strategy: :one_for_one, name: IEx.Supervisor) end end diff --git a/lib/iex/lib/iex/autocomplete.ex b/lib/iex/lib/iex/autocomplete.ex index ca0972160..ec7620c9b 100644 --- a/lib/iex/lib/iex/autocomplete.ex +++ b/lib/iex/lib/iex/autocomplete.ex @@ -1,7 +1,7 @@ defmodule IEx.Autocomplete do @moduledoc false - def expand(expr, server \\ IEx.Server) + def expand(expr, server \\ IEx.Broker) def expand('', server) do expand_variable_or_import("", server) diff --git a/lib/iex/lib/iex/broker.ex b/lib/iex/lib/iex/broker.ex new file mode 100644 index 000000000..45b657f9f --- /dev/null +++ b/lib/iex/lib/iex/broker.ex @@ -0,0 +1,181 @@ +defmodule IEx.Broker do + @moduledoc false + @name IEx.Broker + + @type take_ref :: {takeover_ref :: reference(), server_ref :: reference()} + + use GenServer + + ## Shell API + + @doc """ + Finds the IEx server running inside `:user_drv`, on this node exclusively. + """ + @spec shell :: pid | nil + def shell() do + # Locate top group leader, always registered as user + # can be implemented by group (normally) or user + # (if oldshell or noshell) + if user = Process.whereis(:user) do + case :group.interfaces(user) do + # Old or no shell + [] -> + case :user.interfaces(user) do + [] -> nil + [shell: shell] -> shell + end + + # Get current group from user_drv + [user_drv: user_drv] -> + case :user_drv.interfaces(user_drv) do + [] -> nil + [current_group: group] -> :group.interfaces(group)[:shell] + end + end + end + end + + @doc """ + Finds the evaluator and server running inside `:user_drv`, on this node exclusively. + """ + @spec evaluator :: {evaluator :: pid, server :: pid} | nil + def evaluator() do + if pid = shell() do + {:dictionary, dictionary} = Process.info(pid, :dictionary) + {dictionary[:evaluator], pid} + end + end + + ## Broker API + + def start_link(_) do + GenServer.start_link(__MODULE__, :ok, name: @name) + end + + @doc """ + Registers an IEx server in the broker. + + All instances, except shell ones, are registered. + """ + @spec register(pid) :: :ok + def register(pid) do + GenServer.call(@name, {:register, pid}) + end + + @doc """ + Client responds to a takeover request. + + The broker's PID is needed to support remote shells. + """ + @spec respond(pid, take_ref, boolean()) :: :ok | {:error, :refused | :already_accepted} + def respond(broker_pid, take_ref, true) do + GenServer.call(broker_pid, {:accept, take_ref, Process.group_leader()}) + end + + def respond(broker_pid, take_ref, false) do + GenServer.call(broker_pid, {:refuse, take_ref}) + end + + @doc """ + Client requests a takeover. + """ + @spec take_over(binary, keyword) :: + {:ok, server :: pid, group_leader :: pid} | {:error, :no_iex | :refused} + def take_over(identifier, opts) do + GenServer.call(@name, {:take_over, identifier, opts}, :infinity) + end + + ## Callbacks + + @impl true + def init(:ok) do + state = %{ + servers: %{}, + takeovers: %{} + } + + {:ok, state} + end + + @impl true + def handle_call({:take_over, identifier, opts}, {_, ref} = from, state) do + case servers(state) do + [] -> + {:reply, {:error, :no_iex}, state} + + servers -> + server_refs = + for {server_ref, server_pid} <- servers do + send(server_pid, {:take_over, self(), {ref, server_ref}, identifier, opts}) + server_ref + end + + state = put_in(state.takeovers[ref], {from, server_refs}) + {:noreply, state} + end + end + + def handle_call({:register, pid}, _from, state) do + ref = Process.monitor(pid) + state = put_in(state.servers[ref], pid) + {:reply, :ok, state} + end + + def handle_call({:accept, {ref, _server_ref}, group_leader}, {server, _}, state) do + case pop_in(state.takeovers[ref]) do + {nil, state} -> + {:reply, {:error, :already_accepted}, state} + + {{from, _}, state} -> + GenServer.reply(from, {:ok, server, group_leader}) + {:reply, :ok, state} + end + end + + def handle_call({:refuse, {ref, server_ref}}, _from, state) do + if takeover = state.takeovers[ref] do + {:reply, {:error, :refused}, refuse(state, ref, takeover, server_ref)} + else + {:reply, {:error, :refused}, state} + end + end + + @impl true + def handle_info({:DOWN, server_ref, _, _, _}, state) do + {_pid, state} = pop_in(state.servers[server_ref]) + + state = + Enum.reduce(state.takeovers, state, fn {ref, takeover}, state -> + refuse(state, ref, takeover, server_ref) + end) + + {:noreply, state} + end + + defp refuse(state, ref, {from, server_refs}, server_ref) do + case List.delete(server_refs, server_ref) do + [] -> + {_, state} = pop_in(state.takeovers[ref]) + GenServer.reply(from, {:error, :refused}) + state + + server_refs -> + put_in(state.takeovers[ref], {from, server_refs}) + end + end + + defp servers(state) do + if pid = local_or_remote_shell() do + [{Process.monitor(pid), pid} | Enum.to_list(state.servers)] + else + Enum.to_list(state.servers) + end + end + + defp local_or_remote_shell() do + Enum.find_value([node() | Node.list()], fn node -> + server = :rpc.call(node, IEx.Broker, :shell, []) + if is_pid(server), do: server + end) + end +end diff --git a/lib/iex/lib/iex/evaluator.ex b/lib/iex/lib/iex/evaluator.ex index 3c67c5360..543924826 100644 --- a/lib/iex/lib/iex/evaluator.ex +++ b/lib/iex/lib/iex/evaluator.ex @@ -14,6 +14,9 @@ defmodule IEx.Evaluator do old_leader = Process.group_leader() Process.group_leader(self(), leader) + old_server = Process.get(:iex_server) + Process.put(:iex_server, server) + evaluator = Process.get(:iex_evaluator) Process.put(:iex_evaluator, command) @@ -25,6 +28,12 @@ defmodule IEx.Evaluator do after Process.group_leader(self(), old_leader) + if old_server do + Process.put(:iex_server, old_server) + else + Process.delete(:iex_server) + end + cond do is_nil(evaluator) -> Process.delete(:iex_evaluator) diff --git a/lib/iex/lib/iex/helpers.ex b/lib/iex/lib/iex/helpers.ex index 421f3f4de..5829edcec 100644 --- a/lib/iex/lib/iex/helpers.ex +++ b/lib/iex/lib/iex/helpers.ex @@ -828,8 +828,8 @@ defmodule IEx.Helpers do Respawns the current shell by starting a new shell process. """ def respawn do - if whereis = IEx.Server.whereis() do - send(whereis, {:respawn, self()}) + if iex_server = Process.get(:iex_server) do + send(iex_server, {:respawn, self()}) end dont_display_result() @@ -852,8 +852,8 @@ defmodule IEx.Helpers do """ @doc since: "1.5.0" def continue do - if whereis = IEx.Server.whereis() do - send(whereis, {:continue, self()}) + if iex_server = Process.get(:iex_server) do + send(iex_server, {:continue, self()}) end dont_display_result() diff --git a/lib/iex/lib/iex/pry.ex b/lib/iex/lib/iex/pry.ex index c38243565..81e6c58cb 100644 --- a/lib/iex/lib/iex/pry.ex +++ b/lib/iex/lib/iex/pry.ex @@ -69,9 +69,9 @@ defmodule IEx.Pry do end # We cannot use colors because IEx may be off - case IEx.Server.take_over(request, opts) do - :ok -> - :ok + case IEx.Broker.take_over(request, [evaluator: self()] ++ opts) do + {:ok, server, group_leader} -> + IEx.Evaluator.init(:no_ack, server, group_leader, opts) {:error, :no_iex} -> extra = diff --git a/lib/iex/lib/iex/server.ex b/lib/iex/lib/iex/server.ex index 3fed39028..3feeb0c92 100644 --- a/lib/iex/lib/iex/server.ex +++ b/lib/iex/lib/iex/server.ex @@ -19,55 +19,6 @@ defmodule IEx.Server do """ @doc """ - Finds the IEx server running inside `:user_drv`, on this or another node. - """ - @spec whereis :: pid | nil - def whereis() do - Enum.find_value([node() | Node.list()], fn node -> - server = :rpc.call(node, IEx.Server, :local, []) - if is_pid(server), do: server - end) - end - - @doc """ - Finds the IEx server running inside `:user_drv`, on this node exclusively. - """ - @spec local :: pid | nil - def local() do - # Locate top group leader, always registered as user - # can be implemented by group (normally) or user - # (if oldshell or noshell) - if user = Process.whereis(:user) do - case :group.interfaces(user) do - # Old or no shell - [] -> - case :user.interfaces(user) do - [] -> nil - [shell: shell] -> shell - end - - # Get current group from user_drv - [user_drv: user_drv] -> - case :user_drv.interfaces(user_drv) do - [] -> nil - [current_group: group] -> :group.interfaces(group)[:shell] - end - end - end - end - - @doc """ - Finds the evaluator and server running inside `:user_drv`, on this node exclusively. - """ - @spec evaluator :: {evaluator :: pid, server :: pid} | nil - def evaluator() do - if pid = IEx.Server.local() do - {:dictionary, dictionary} = Process.info(pid, :dictionary) - {dictionary[:evaluator], pid} - end - end - - @doc """ Starts a new IEx server session. The accepted options are: @@ -77,8 +28,13 @@ defmodule IEx.Server do * `:binding` - an initial set of variables for the evaluator """ - @spec start(keyword) :: :ok - def start(opts) when is_list(opts) do + @spec run(keyword) :: :ok + def run(opts) when is_list(opts) do + IEx.Broker.register(self()) + run_without_registration(opts) + end + + defp run_without_registration(opts) do Process.flag(:trap_exit, true) IO.puts( @@ -91,60 +47,38 @@ defmodule IEx.Server do ## Private APIs - # `start/1` with a callback. The server is spawned only after - # the callback is done. + # Starts IEx to run directly from the Erlang shell. + # + # The server is spawned only after the callback is done. # # If there is any takeover during the callback execution # we spawn a new server for it without waiting for its # conclusion. @doc false - @spec start(keyword, {module, atom, [any]}) :: :ok - def start(opts, {m, f, a}) do + @spec run_from_shell(keyword, {module, atom, [any]}) :: :ok + def run_from_shell(opts, {m, f, a}) do Process.flag(:trap_exit, true) {pid, ref} = spawn_monitor(m, f, a) - start_loop(opts, pid, ref) + shell_loop(opts, pid, ref) end - defp start_loop(opts, pid, ref) do + defp shell_loop(opts, pid, ref) do receive do - {:take, take_pid, take_identifier, take_ref, take_opts} -> - if allow_take?(take_identifier) do - send(take_pid, {take_ref, Process.group_leader()}) - start(take_opts) + {:take_over, take_pid, take_ref, take_identifier, take_opts} -> + if take_over?(take_pid, take_ref, take_identifier) do + run_without_registration(take_opts) else - send(take_pid, {take_ref, nil}) - start_loop(opts, pid, ref) + shell_loop(opts, pid, ref) end {:DOWN, ^ref, :process, ^pid, :normal} -> - start(opts) + run_without_registration(opts) {:DOWN, ^ref, :process, ^pid, _reason} -> :ok end end - # Requests to take over the given shell from the current process. - @doc false - @spec take_over(binary, keyword) :: :ok | {:error, :no_iex} | {:error, :refused} - def take_over(identifier, opts, server \\ whereis()) do - if is_nil(server) do - {:error, :no_iex} - else - ref = make_ref() - opts = [evaluator: self()] ++ opts - send(server, {:take, self(), identifier, ref, opts}) - - receive do - {^ref, nil} -> - {:error, :refused} - - {^ref, leader} -> - IEx.Evaluator.init(:no_ack, server, leader, opts) - end - end - end - # Starts an evaluator using the provided options. @doc false @spec start_evaluator(keyword) :: pid @@ -166,11 +100,11 @@ defmodule IEx.Server do :ok end - defp restart(opts, evaluator, evaluator_ref, input) do + defp rerun(opts, evaluator, evaluator_ref, input) do kill_input(input) IO.puts("") stop_evaluator(evaluator, evaluator_ref) - start(opts) + run_without_registration(opts) end defp loop(state, evaluator, evaluator_ref) do @@ -231,7 +165,7 @@ defmodule IEx.Server do # A take process may also happen if the evaluator dies, # then a new evaluator is created to replace the dead one. defp handle_take_over( - {:take, other, identifier, ref, opts}, + {:take_over, take_pid, take_ref, take_identifier, opts}, state, evaluator, evaluator_ref, @@ -240,16 +174,17 @@ defmodule IEx.Server do ) do cond do evaluator == opts[:evaluator] -> - send(other, {ref, Process.group_leader()}) - kill_input(input) - loop(iex_state(opts), evaluator, evaluator_ref) + if take_over?(take_pid, take_ref, true) do + kill_input(input) + loop(iex_state(opts), evaluator, evaluator_ref) + else + callback.(state) + end - allow_take?(identifier) -> - send(other, {ref, Process.group_leader()}) - restart(opts, evaluator, evaluator_ref, input) + take_over?(take_pid, take_ref, take_identifier) -> + rerun(opts, evaluator, evaluator_ref, input) true -> - send(other, {ref, nil}) callback.(state) end end @@ -273,7 +208,7 @@ defmodule IEx.Server do end defp handle_take_over({:respawn, evaluator}, _state, evaluator, evaluator_ref, input, _callback) do - restart([], evaluator, evaluator_ref, input) + rerun([], evaluator, evaluator_ref, input) end defp handle_take_over({:continue, evaluator}, state, evaluator, evaluator_ref, input, _callback) do @@ -290,7 +225,7 @@ defmodule IEx.Server do input, _callback ) do - restart([], evaluator, evaluator_ref, input) + rerun([], evaluator, evaluator_ref, input) end defp handle_take_over( @@ -311,21 +246,35 @@ defmodule IEx.Server do io_error("** (IEx.Error) #{type} when printing EXIT message: #{inspect(detail)}") end - restart([], evaluator, evaluator_ref, input) + rerun([], evaluator, evaluator_ref, input) end defp handle_take_over(_, state, _evaluator, _evaluator_ref, _input, callback) do callback.(state) end - defp kill_input(nil), do: :ok - defp kill_input(input), do: Process.exit(input, :kill) + defp take_over?(take_pid, take_ref, take_identifier) when is_binary(take_identifier) do + message = IEx.color(:eval_interrupt, "#{take_identifier}\nAllow? [Yn] ") + take_over?(take_pid, take_ref, yes?(IO.gets(:stdio, message))) + end + + defp take_over?(take_pid, take_ref, take_response) do + case IEx.Broker.respond(take_pid, take_ref, take_response) do + :ok -> + true + + {:error, :refused} -> + false - defp allow_take?(identifier) do - message = IEx.color(:eval_interrupt, "#{identifier}\nAllow? [Yn] ") - yes?(IO.gets(:stdio, message)) + {:error, :already_accepted} -> + io_error("** session was already accepted elsewhere") + false + end end + defp kill_input(nil), do: :ok + defp kill_input(input), do: Process.exit(input, :kill) + defp yes?(string) do is_binary(string) and String.trim(string) in ["", "y", "Y", "yes", "YES", "Yes"] end diff --git a/lib/iex/test/iex/interaction_test.exs b/lib/iex/test/iex/interaction_test.exs index 591d1f53f..c53c0bd92 100644 --- a/lib/iex/test/iex/interaction_test.exs +++ b/lib/iex/test/iex/interaction_test.exs @@ -5,7 +5,7 @@ defmodule IEx.InteractionTest do test "whole output" do assert capture_io("IO.puts \"Hello world\"", fn -> - IEx.Server.start([dot_iex_path: ""], {IEx, :dont_display_result, []}) + IEx.Server.run(dot_iex_path: "") end) =~ "Interactive Elixir (#{System.version()}) - press Ctrl+C to exit (type h() ENTER for help)" <> "\niex(1)> Hello world\n:ok\niex(2)>" diff --git a/lib/iex/test/iex/server_test.exs b/lib/iex/test/iex/server_test.exs index 106edab7b..f387a6b30 100644 --- a/lib/iex/test/iex/server_test.exs +++ b/lib/iex/test/iex/server_test.exs @@ -3,65 +3,167 @@ Code.require_file("../test_helper.exs", __DIR__) defmodule IEx.ServerTest do use IEx.Case + require IEx + describe "options" do test "prefix" do assert capture_io(fn -> - boot(prefix: "pry") + IEx.Server.run(prefix: "pry") end) =~ "pry(1)> " end test "env" do assert capture_io("__ENV__.file", fn -> - boot(env: __ENV__) + IEx.Server.run(env: __ENV__) end) =~ "server_test.exs" end end - describe "take_over" do - test "allows takeover of the shell during boot" do - assert capture_io("Y\na+b", fn -> - server = self() + describe "pry" do + test "no sessions" do + assert capture_io(fn -> + assert IEx.pry() == {:error, :no_iex} + end) =~ "Is an IEx shell running?" + end - boot([], fn -> - opts = [prefix: "dbg", binding: [a: 1, b: 2]] - IEx.Server.take_over("iex:13", opts, server) - end) - end) =~ "dbg(1)> " + test "inside evaluator itself" do + assert capture_iex("require IEx; IEx.pry") =~ "Break reached" end - test "continues if takeover is refused" do - assert capture_io("N\n", fn -> - server = self() + test "outside of the evaluator with acceptance", config do + Process.register(self(), config.test) - boot([], fn -> - opts = [prefix: "dbg", binding: [a: 1, b: 2]] - IEx.Server.take_over("iex:13", opts, server) - end) - end) =~ "iex(1)> " + {server, evaluator} = pry_session(config.test, "Y\niex_context") + client = pry_request([server]) + send(evaluator, :run) + + assert Task.await(server) =~ ":inside_pry" + assert Task.await(client) == :ok end - test "fails if callback during boot fails" do - assert capture_io(fn -> - boot([], fn -> exit(0) end) - end) == "" + test "outside of the evaluator with refusal", config do + Process.register(self(), config.test) + + {server, evaluator} = pry_session(config.test, "N\niex_context") + client = pry_request([server]) + send(evaluator, :run) + + assert Task.await(client) == {:error, :refused} + assert Task.await(server) =~ "undefined function iex_context" end - test "fails when there is no shell" do - assert IEx.Server.take_over("iex:13", []) == {:error, :no_iex} + test "outside of the evaluator with crash", config do + Process.register(self(), config.test) + + {server, _evaluator} = pry_session(config.test, "iex_context") + client = pry_request([server]) + + _ = Task.shutdown(server, :brutal_kill) + assert Task.await(client) == {:error, :refused} end - end - test "pry wraps around takeover" do - require IEx + test "outside of the evaluator with double acceptance", config do + Process.register(self(), config.test) + + {server1, evaluator1} = pry_session(config.test, "Y\niex_context") + {server2, evaluator2} = pry_session(config.test, "Y\niex_context") + client = pry_request([server1, server2]) + + send(evaluator1, :run) + send(evaluator2, :run) + reply1 = Task.await(server1) + reply2 = Task.await(server2) + + {accepted, refused} = + if reply1 =~ ":inside_pry", do: {reply1, reply2}, else: {reply2, reply1} + + assert accepted =~ ":inside_pry" + assert refused =~ "** session was already accepted elsewhere" + assert refused =~ "undefined function iex_context" + + assert Task.await(client) == :ok + end + + test "outside of the evaluator with double refusal", config do + Process.register(self(), config.test) + + {server1, evaluator1} = pry_session(config.test, "N\niex_context") + {server2, evaluator2} = pry_session(config.test, "N\niex_context") + client = pry_request([server1, server2]) + + send(evaluator1, :run) + send(evaluator2, :run) + reply1 = Task.await(server1) + reply2 = Task.await(server2) - assert capture_io(fn -> - assert IEx.pry() == {:error, :no_iex} - end) =~ "Is an IEx shell running?" + assert reply1 =~ "undefined function iex_context" + assert reply2 =~ "undefined function iex_context" + + assert Task.await(client) == {:error, :refused} + end + + test "outside of the evaluator with acceptance and then refusal", config do + Process.register(self(), config.test) + + {server1, evaluator1} = pry_session(config.test, "Y\niex_context") + {server2, evaluator2} = pry_session(config.test, "N\niex_context") + client = pry_request([server1, server2]) + + send(evaluator1, :run) + send(evaluator2, :run) + assert Task.await(server1) =~ ":inside_pry" + assert Task.await(server2) =~ "undefined function iex_context" + + assert Task.await(client) == :ok + end + + test "outside of the evaluator with refusal and then acceptance", config do + Process.register(self(), config.test) + + {server1, evaluator1} = pry_session(config.test, "N\niex_context") + {server2, evaluator2} = pry_session(config.test, "Y\niex_context") + client = pry_request([server1, server2]) + + send(evaluator1, :run) + send(evaluator2, :run) + assert Task.await(server1) =~ "undefined function iex_context" + assert Task.await(server2) =~ ":inside_pry" + + assert Task.await(client) == :ok + end end # Helpers - defp boot(opts, callback \\ fn -> nil end) do - IEx.Server.start(Keyword.merge([dot_iex_path: ""], opts), {:erlang, :apply, [callback, []]}) + defp pry_session(name, session) do + task = + Task.async(fn -> + capture_iex(""" + send(#{inspect(name)}, {:running, self()}) + receive do: (:run -> :ok) + #{session} + """) + end) + + assert_receive {:running, evaluator} + {task, evaluator} + end + + defp pry_request(sessions) do + :erlang.trace(Process.whereis(IEx.Broker), true, [:receive, tracer: self()]) + patterns = for %{pid: pid} <- sessions, do: {[:_, pid, :_], [], []} + :erlang.trace_pattern(:receive, patterns, []) + + task = + Task.async(fn -> + iex_context = :inside_pry + IEx.pry() + end) + + for _ <- sessions do + assert_receive {:trace, _, :receive, _} + end + + task end end diff --git a/lib/iex/test/test_helper.exs b/lib/iex/test/test_helper.exs index 8cc7a407b..137bbd8a0 100644 --- a/lib/iex/test/test_helper.exs +++ b/lib/iex/test/test_helper.exs @@ -57,14 +57,14 @@ defmodule IEx.Case do Options, if provided, will be set before the eval loop is started. If you provide server options, it will be passed to - IEx.Server.start to be used in the normal .iex loading process. + IEx.Server.run to be used in the normal .iex loading process. """ def capture_iex(input, options \\ [], server_options \\ [], capture_prompt \\ false) do IEx.configure(options) ExUnit.CaptureIO.capture_io([input: input, capture_prompt: capture_prompt], fn -> server_options = Keyword.put_new(server_options, :dot_iex_path, "") - IEx.Server.start(server_options, {IEx, :dont_display_result, []}) + IEx.Server.run(server_options) end) |> strip_iex end |