Skip to content

Commit

Permalink
Make code_reload act regardless of diagnostics (#1423)
Browse files Browse the repository at this point in the history
  • Loading branch information
fabjan authored Dec 21, 2023
1 parent 16eea7d commit 7163398
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 130 deletions.
53 changes: 53 additions & 0 deletions apps/els_lsp/src/els_code_reload.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
-module(els_code_reload).

-include("els_lsp.hrl").
-include_lib("kernel/include/logger.hrl").

-export([
maybe_compile_and_load/1
]).

-spec maybe_compile_and_load(uri()) -> ok.
maybe_compile_and_load(Uri) ->
Ext = filename:extension(Uri),
case els_config:get(code_reload) of
#{"node" := NodeStr} when Ext == <<".erl">> ->
Node = els_utils:compose_node_name(
NodeStr,
els_config_runtime:get_name_type()
),
Module = els_uri:module(Uri),
case rpc:call(Node, code, is_sticky, [Module]) of
true -> ok;
_ -> handle_rpc_result(rpc:call(Node, c, c, [Module]), Module)
end;
_ ->
ok
end.

-spec handle_rpc_result(term() | {badrpc, term()}, atom()) -> ok.
handle_rpc_result({ok, Module}, _) ->
Msg = io_lib:format("code_reload success for: ~s", [Module]),
els_server:send_notification(
<<"window/showMessage">>,
#{
type => ?MESSAGE_TYPE_INFO,
message => els_utils:to_binary(Msg)
}
);
handle_rpc_result(Err, Module) ->
?LOG_INFO(
"[code_reload] code_reload using c:c/1 crashed with: ~p",
[Err]
),
Msg = io_lib:format(
"code_reload swap crashed for: ~s with: ~w",
[Module, Err]
),
els_server:send_notification(
<<"window/showMessage">>,
#{
type => ?MESSAGE_TYPE_ERROR,
message => els_utils:to_binary(Msg)
}
).
49 changes: 1 addition & 48 deletions apps/els_lsp/src/els_compiler_diagnostics.erl
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ source() ->

-spec on_complete(uri(), [els_diagnostics:diagnostic()]) -> ok.
on_complete(Uri, Diagnostics) ->
?MODULE:telemetry(Uri, Diagnostics),
maybe_compile_and_load(Uri, Diagnostics).
?MODULE:telemetry(Uri, Diagnostics).

%%==============================================================================
%% Internal Functions
Expand Down Expand Up @@ -823,52 +822,6 @@ load_dependency(Module, IncludingPath) ->
end,
{Old, Diagnostics}.

-spec maybe_compile_and_load(uri(), [els_diagnostics:diagnostic()]) -> ok.
maybe_compile_and_load(Uri, [] = _CDiagnostics) ->
case els_config:get(code_reload) of
#{"node" := NodeStr} ->
Node = els_utils:compose_node_name(
NodeStr,
els_config_runtime:get_name_type()
),
Module = els_uri:module(Uri),
case rpc:call(Node, code, is_sticky, [Module]) of
true -> ok;
_ -> handle_rpc_result(rpc:call(Node, c, c, [Module]), Module)
end;
disabled ->
ok
end;
maybe_compile_and_load(_Uri, _CDiagnostics) ->
ok.

-spec handle_rpc_result(term() | {badrpc, term()}, atom()) -> ok.
handle_rpc_result({ok, Module}, _) ->
Msg = io_lib:format("code_reload success for: ~s", [Module]),
els_server:send_notification(
<<"window/showMessage">>,
#{
type => ?MESSAGE_TYPE_INFO,
message => els_utils:to_binary(Msg)
}
);
handle_rpc_result(Err, Module) ->
?LOG_INFO(
"[code_reload] code_reload using c:c/1 crashed with: ~p",
[Err]
),
Msg = io_lib:format(
"code_reload swap crashed for: ~s with: ~w",
[Module, Err]
),
els_server:send_notification(
<<"window/showMessage">>,
#{
type => ?MESSAGE_TYPE_ERROR,
message => els_utils:to_binary(Msg)
}
).

%% @doc Return the compile options from the compile_info chunk
-spec compile_options(atom()) -> [any()].
compile_options(Module) ->
Expand Down
1 change: 1 addition & 0 deletions apps/els_lsp/src/els_text_synchronization_provider.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ handle_request({did_change, Params}) ->
handle_request({did_save, Params}) ->
ok = els_text_synchronization:did_save(Params),
#{<<"textDocument">> := #{<<"uri">> := Uri}} = Params,
ok = els_code_reload:maybe_compile_and_load(Uri),
{diagnostics, Uri, els_diagnostics:run_diagnostics(Uri)};
handle_request({did_close, Params}) ->
ok = els_text_synchronization:did_close(Params),
Expand Down
133 changes: 133 additions & 0 deletions apps/els_lsp/test/els_code_reload_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
-module(els_code_reload_SUITE).

%% CT Callbacks
-export([
suite/0,
init_per_suite/1,
end_per_suite/1,
init_per_testcase/2,
end_per_testcase/2,
all/0
]).

%% Test cases
-export([
code_reload/1,
code_reload_sticky_mod/1
]).

%%==============================================================================
%% Includes
%%==============================================================================
-include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/assert.hrl").

%%==============================================================================
%% Types
%%==============================================================================
-type config() :: [{atom(), any()}].

%%==============================================================================
%% CT Callbacks
%%==============================================================================
-spec suite() -> [tuple()].
suite() ->
[{timetrap, {seconds, 30}}].

-spec all() -> [atom()].
all() ->
els_test_utils:all(?MODULE).

-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
els_test_utils:init_per_suite(Config).

-spec end_per_suite(config()) -> ok.
end_per_suite(Config) ->
els_test_utils:end_per_suite(Config).

-spec init_per_testcase(atom(), config()) -> config().
init_per_testcase(TestCase, Config) ->
mock_rpc(),
mock_code_reload_enabled(),
els_test_utils:init_per_testcase(TestCase, Config).

-spec end_per_testcase(atom(), config()) -> ok.
end_per_testcase(TestCase, Config) ->
unmock_rpc(),
unmock_code_reload_enabled(),
els_test_utils:end_per_testcase(TestCase, Config).

%%==============================================================================
%% Testcases
%%==============================================================================

-spec code_reload(config()) -> ok.
code_reload(Config) ->
Uri = ?config(diagnostics_uri, Config),
Module = els_uri:module(Uri),
ok = els_code_reload:maybe_compile_and_load(Uri),
{ok, HostName} = inet:gethostname(),
NodeName = list_to_atom("fakenode@" ++ HostName),
?assert(meck:called(rpc, call, [NodeName, c, c, [Module]])),
ok.

-spec code_reload_sticky_mod(config()) -> ok.
code_reload_sticky_mod(Config) ->
Uri = ?config(diagnostics_uri, Config),
Module = els_uri:module(Uri),
{ok, HostName} = inet:gethostname(),
NodeName = list_to_atom("fakenode@" ++ HostName),
meck:expect(
rpc,
call,
fun
(PNode, code, is_sticky, [_]) when PNode =:= NodeName ->
true;
(Node, Mod, Fun, Args) ->
meck:passthrough([Node, Mod, Fun, Args])
end
),
ok = els_code_reload:maybe_compile_and_load(Uri),
?assert(meck:called(rpc, call, [NodeName, code, is_sticky, [Module]])),
?assertNot(meck:called(rpc, call, [NodeName, c, c, [Module]])),
ok.

%%==============================================================================
%% Internal Functions
%%==============================================================================

mock_rpc() ->
meck:new(rpc, [passthrough, no_link, unstick]),
{ok, HostName} = inet:gethostname(),
NodeName = list_to_atom("fakenode@" ++ HostName),
meck:expect(
rpc,
call,
fun
(PNode, c, c, [Module]) when PNode =:= NodeName ->
{ok, Module};
(Node, Mod, Fun, Args) ->
meck:passthrough([Node, Mod, Fun, Args])
end
).

unmock_rpc() ->
meck:unload(rpc).

mock_code_reload_enabled() ->
meck:new(els_config, [passthrough, no_link]),
meck:expect(
els_config,
get,
fun
(code_reload) ->
{ok, HostName} = inet:gethostname(),
#{"node" => "fakenode@" ++ HostName};
(Key) ->
meck:passthrough([Key])
end
).

unmock_code_reload_enabled() ->
meck:unload(els_config).
82 changes: 0 additions & 82 deletions apps/els_lsp/test/els_diagnostics_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@
use_long_names_no_domain/1,
use_long_names_custom_hostname/1,
epp_with_nonexistent_macro/1,
code_reload/1,
code_reload_sticky_mod/1,
elvis/1,
escript/1,
escript_warnings/1,
Expand Down Expand Up @@ -89,13 +87,6 @@ end_per_suite(Config) ->
els_test_utils:end_per_suite(Config).

-spec init_per_testcase(atom(), config()) -> config().
init_per_testcase(TestCase, Config) when
TestCase =:= code_reload orelse
TestCase =:= code_reload_sticky_mod
->
mock_rpc(),
mock_code_reload_enabled(),
els_test_utils:init_per_testcase(TestCase, Config);
init_per_testcase(TestCase, Config) when
TestCase =:= atom_typo
->
Expand Down Expand Up @@ -212,13 +203,6 @@ end_per_testcase(TestCase, Config) when
els_test_utils:end_per_testcase(TestCase, Config),
els_mock_diagnostics:teardown(),
ok;
end_per_testcase(TestCase, Config) when
TestCase =:= code_reload orelse
TestCase =:= code_reload_sticky_mod
->
unmock_rpc(),
unmock_code_reload_enabled(),
els_test_utils:end_per_testcase(TestCase, Config);
end_per_testcase(TestCase, Config) when
TestCase =:= crossref orelse
TestCase =:= crossref_pseudo_functions orelse
Expand Down Expand Up @@ -779,37 +763,6 @@ escript_errors(_Config) ->
Hints = [],
els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints).

-spec code_reload(config()) -> ok.
code_reload(Config) ->
Uri = ?config(diagnostics_uri, Config),
Module = els_uri:module(Uri),
ok = els_compiler_diagnostics:on_complete(Uri, []),
{ok, HostName} = inet:gethostname(),
NodeName = list_to_atom("fakenode@" ++ HostName),
?assert(meck:called(rpc, call, [NodeName, c, c, [Module]])),
ok.

-spec code_reload_sticky_mod(config()) -> ok.
code_reload_sticky_mod(Config) ->
Uri = ?config(diagnostics_uri, Config),
Module = els_uri:module(Uri),
{ok, HostName} = inet:gethostname(),
NodeName = list_to_atom("fakenode@" ++ HostName),
meck:expect(
rpc,
call,
fun
(PNode, code, is_sticky, [_]) when PNode =:= NodeName ->
true;
(Node, Mod, Fun, Args) ->
meck:passthrough([Node, Mod, Fun, Args])
end
),
ok = els_compiler_diagnostics:on_complete(Uri, []),
?assert(meck:called(rpc, call, [NodeName, code, is_sticky, [Module]])),
?assertNot(meck:called(rpc, call, [NodeName, c, c, [Module]])),
ok.

-spec crossref(config()) -> ok.
crossref(_Config) ->
Path = src_path("diagnostics_xref.erl"),
Expand Down Expand Up @@ -1097,41 +1050,6 @@ unused_macros_refactorerl(_Config) ->
%% Internal Functions
%%==============================================================================

mock_rpc() ->
meck:new(rpc, [passthrough, no_link, unstick]),
{ok, HostName} = inet:gethostname(),
NodeName = list_to_atom("fakenode@" ++ HostName),
meck:expect(
rpc,
call,
fun
(PNode, c, c, [Module]) when PNode =:= NodeName ->
{ok, Module};
(Node, Mod, Fun, Args) ->
meck:passthrough([Node, Mod, Fun, Args])
end
).

unmock_rpc() ->
meck:unload(rpc).

mock_code_reload_enabled() ->
meck:new(els_config, [passthrough, no_link]),
meck:expect(
els_config,
get,
fun
(code_reload) ->
{ok, HostName} = inet:gethostname(),
#{"node" => "fakenode@" ++ HostName};
(Key) ->
meck:passthrough([Key])
end
).

unmock_code_reload_enabled() ->
meck:unload(els_config).

mock_compiler_telemetry_enabled() ->
meck:new(els_config, [passthrough, no_link]),
meck:expect(
Expand Down

0 comments on commit 7163398

Please sign in to comment.