Skip to content

Improve test reliability #488

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 3 commits into from
Feb 15, 2021
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
4 changes: 4 additions & 0 deletions apps/elixir_ls_debugger/lib/debugger/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,10 @@ defmodule ElixirLS.Debugger.Server do
end

defp launch_task(task, args) do
# This fixes a race condition in the tests and likely improves reliability when using the
# debugger as well.
Process.sleep(100)

Mix.Task.run(task, args)

# Starting from Elixir 1.9 Mix.Task.run will return so we need to sleep our
Expand Down
46 changes: 33 additions & 13 deletions apps/elixir_ls_debugger/test/debugger_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ defmodule ElixirLS.Debugger.ServerTest do
end
end

@tag :fixture
test "basic debugging", %{server: server} do
in_fixture(__DIR__, "mix_project", fn ->
Server.receive_packet(server, initialize_req(1, %{}))
Expand All @@ -100,6 +101,7 @@ defmodule ElixirLS.Debugger.ServerTest do
"request" => "launch",
"type" => "mix_task",
"task" => "test",
"taskArgs" => ["--only", "quadruple"],
"projectDir" => File.cwd!()
})
)
Expand Down Expand Up @@ -132,7 +134,8 @@ defmodule ElixirLS.Debugger.ServerTest do
"allThreadsStopped" => false,
"reason" => "breakpoint",
"threadId" => thread_id
})
}),
5_000

Server.receive_packet(server, stacktrace_req(7, thread_id))

Expand Down Expand Up @@ -191,6 +194,7 @@ defmodule ElixirLS.Debugger.ServerTest do
end)
end

@tag :fixture
test "handles invalid requests", %{server: server} do
in_fixture(__DIR__, "mix_project", fn ->
Server.receive_packet(server, initialize_req(1, %{}))
Expand Down Expand Up @@ -234,7 +238,8 @@ defmodule ElixirLS.Debugger.ServerTest do
"allThreadsStopped" => false,
"reason" => "breakpoint",
"threadId" => thread_id
})
}),
1_000

Server.receive_packet(server, stacktrace_req(7, "not existing"))

Expand Down Expand Up @@ -341,6 +346,7 @@ defmodule ElixirLS.Debugger.ServerTest do
end)
end

@tag :fixture
test "notifies about process exit", %{server: server} do
in_fixture(__DIR__, "mix_project", fn ->
Server.receive_packet(server, initialize_req(1, %{}))
Expand Down Expand Up @@ -377,7 +383,7 @@ defmodule ElixirLS.Debugger.ServerTest do
assert_receive(response(_, 5, "configurationDone", %{}))

Server.receive_packet(server, request(6, "threads", %{}))
assert_receive(response(_, 6, "threads", %{"threads" => threads}))
assert_receive(response(_, 6, "threads", %{"threads" => threads}), 1_000)
# ensure thread ids are unique
thread_ids = Enum.map(threads, & &1["id"])
assert Enum.count(Enum.uniq(thread_ids)) == Enum.count(thread_ids)
Expand All @@ -389,14 +395,21 @@ defmodule ElixirLS.Debugger.ServerTest do
}),
500

assert_receive event(_, "thread", %{
"reason" => "exited",
"threadId" => ^thread_id
}),
5000
{log, stderr} =
capture_log_and_io(:standard_error, fn ->
assert_receive event(_, "thread", %{
"reason" => "exited",
"threadId" => ^thread_id
}),
5000
end)

assert log =~ "Fixture MixProject expected error"
assert stderr =~ "Fixture MixProject expected error"
end)
end

@tag :fixture
test "notifies about mix task exit", %{server: server} do
in_fixture(__DIR__, "mix_project", fn ->
Server.receive_packet(server, initialize_req(1, %{}))
Expand Down Expand Up @@ -444,11 +457,17 @@ defmodule ElixirLS.Debugger.ServerTest do
}),
5000

assert_receive event(_, "thread", %{
"reason" => "exited",
"threadId" => ^thread_id
}),
5000
{log, io} =
capture_log_and_io(:stderr, fn ->
assert_receive event(_, "thread", %{
"reason" => "exited",
"threadId" => ^thread_id
}),
5000
end)

assert log =~ "Fixture MixProject raise for exit_self/0"
assert io =~ "Fixture MixProject raise for exit_self/0"

assert_receive event(_, "exited", %{
"exitCode" => 1
Expand All @@ -460,6 +479,7 @@ defmodule ElixirLS.Debugger.ServerTest do
end)
end

@tag :fixture
test "sets breakpoints in erlang modules", %{server: server} do
in_fixture(__DIR__, "mix_project", fn ->
Server.receive_packet(server, initialize_req(1, %{}))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule MixProject do
Task.start(fn ->
Task.start_link(fn ->
Process.sleep(1000)
raise ArgumentError
raise "Fixture MixProject expected error"
end)

Process.sleep(:infinity)
Expand All @@ -23,7 +23,7 @@ defmodule MixProject do
def exit_self do
Task.start_link(fn ->
Process.sleep(1000)
raise ArgumentError
raise "Fixture MixProject raise for exit_self/0"
end)

Process.sleep(:infinity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ defmodule MixProjectTest do
use ExUnit.Case
doctest MixProject

@tag :double
test "double" do
assert MixProject.double(2) == 4
end

@tag :quadruple
test "quadruple" do
assert MixProject.quadruple(2) == 8
end
Expand Down
10 changes: 10 additions & 0 deletions apps/elixir_ls_utils/lib/output_device.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ defmodule ElixirLS.Utils.OutputDevice do
Task.start_link(fn -> loop({device, output_fn}) end)
end

def child_spec(arguments) do
%{
id: __MODULE__,
start: {__MODULE__, :start_link, arguments},
type: :worker,
restart: :permanent,
shutdown: 500
}
end

def get_opts, do: @opts

## Implementation
Expand Down
14 changes: 10 additions & 4 deletions apps/elixir_ls_utils/test/output_device_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,16 @@ defmodule ElixirLS.Utils.OutputDeviceTest do
end

setup do
{:ok, fake_user} = FakeOutput.start_link([])
{:ok, fake_wire_protocol} = FakeWireProtocol.start_link([])
{:ok, output_device} = OutputDevice.start_link(fake_user, &FakeWireProtocol.send/1)
{:ok, output_device_error} = OutputDevice.start_link(fake_user, fn _ -> {:error, :emfile} end)
fake_user = start_supervised!({FakeOutput, []})
fake_wire_protocol = start_supervised!({FakeWireProtocol, []})

output_device =
start_supervised!({OutputDevice, [fake_user, &FakeWireProtocol.send/1]}, id: :output_device)

output_device_error =
start_supervised!({OutputDevice, [fake_user, fn _ -> {:error, :emfile} end]},
id: :output_device_error
)

{:ok,
%{
Expand Down
10 changes: 10 additions & 0 deletions apps/elixir_ls_utils/test/placeholder_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule ElixirLS.Utils.PlaceholderTest do
use ExUnit.Case, async: true

@tag :fixture
test "pretend fixture" do
# This test is included to prevent the following error:
# > The --only option was given to "mix test" but no test was executed
:ok
end
end
18 changes: 18 additions & 0 deletions apps/elixir_ls_utils/test/support/mix_test.case.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ defmodule ElixirLS.Utils.MixTest.Case do
previous = :code.all_loaded()
project_stack = clear_project_stack!()

ExUnit.CaptureLog.capture_log(fn -> Application.stop(:mix) end)
Application.start(:mix)

try do
File.cd!(dest, function)
after
Expand Down Expand Up @@ -148,4 +151,19 @@ defmodule ElixirLS.Utils.MixTest.Case do

module.clear_cache()
end

def capture_log_and_io(device, fun) when is_function(fun, 0) do
# Logger gets stopped during some tests so restart it to be able to capture logs (and keept the
# test output clean)
Application.ensure_started(:logger)

log =
ExUnit.CaptureLog.capture_log(fn ->
io = ExUnit.CaptureIO.capture_io(device, fun)
send(self(), {:block_result, io})
end)

assert_received {:block_result, io_result}
{log, io_result}
end
end
7 changes: 7 additions & 0 deletions apps/language_server/test/dialyzer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
{:ok, %{server: server}}
end

@tag slow: true, fixture: true
test "reports diagnostics then clears them once problems are fixed", %{server: server} do
in_fixture(__DIR__, "dialyzer", fn ->
file_a = SourceFile.path_to_uri(Path.absname("lib/a.ex"))
Expand Down Expand Up @@ -91,6 +92,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end

@tag slow: true, fixture: true
test "only analyzes the changed files", %{server: server} do
in_fixture(__DIR__, "dialyzer", fn ->
file_c = SourceFile.path_to_uri(Path.absname("lib/c.ex"))
Expand Down Expand Up @@ -151,6 +153,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end

@tag slow: true, fixture: true
test "reports dialyxir_long formatted error", %{server: server} do
in_fixture(__DIR__, "dialyzer", fn ->
file_a = SourceFile.path_to_uri(Path.absname("lib/a.ex"))
Expand Down Expand Up @@ -204,6 +207,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end

@tag slow: true, fixture: true
test "reports dialyxir_short formatted error", %{server: server} do
in_fixture(__DIR__, "dialyzer", fn ->
file_a = SourceFile.path_to_uri(Path.absname("lib/a.ex"))
Expand Down Expand Up @@ -248,6 +252,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end

@tag slow: true, fixture: true
test "reports dialyzer_formatted error", %{server: server} do
in_fixture(__DIR__, "dialyzer", fn ->
file_a = SourceFile.path_to_uri(Path.absname("lib/a.ex"))
Expand Down Expand Up @@ -293,6 +298,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end

@tag slow: true, fixture: true
test "reports dialyxir_short error in umbrella", %{server: server} do
in_fixture(__DIR__, "umbrella_dialyzer", fn ->
file_a = SourceFile.path_to_uri(Path.absname("apps/app1/lib/app1.ex"))
Expand Down Expand Up @@ -363,6 +369,7 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
end)
end

@tag slow: true, fixture: true
test "protocol rebuild does not trigger consolidation warnings", %{server: server} do
in_fixture(__DIR__, "protocols", fn ->
root_uri = SourceFile.path_to_uri(File.cwd!())
Expand Down
5 changes: 5 additions & 0 deletions apps/language_server/test/providers/formatting_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule ElixirLS.LanguageServer.Providers.FormattingTest do
alias ElixirLS.LanguageServer.SourceFile
alias ElixirLS.LanguageServer.Test.FixtureHelpers

@tag :fixture
test "Formats a file" do
in_fixture(Path.join(__DIR__, ".."), "formatter", fn ->
path = "lib/file.ex"
Expand Down Expand Up @@ -57,6 +58,7 @@ defmodule ElixirLS.LanguageServer.Providers.FormattingTest do
defp assert_position_type(%{"character" => ch, "line" => line}),
do: is_integer(ch) and is_integer(line)

@tag :fixture
test "returns an error when formatting a file with a syntax error" do
in_fixture(Path.join(__DIR__, ".."), "formatter", fn ->
path = "lib/file.ex"
Expand Down Expand Up @@ -85,6 +87,7 @@ defmodule ElixirLS.LanguageServer.Providers.FormattingTest do
end)
end

@tag :fixture
test "Proper utf-16 format: emoji 😀" do
in_fixture(Path.join(__DIR__, ".."), "formatter", fn ->
path = "lib/file.ex"
Expand Down Expand Up @@ -123,6 +126,7 @@ defmodule ElixirLS.LanguageServer.Providers.FormattingTest do
end)
end

@tag :fixture
test "Proper utf-16 format: emoji 🏳️‍🌈" do
in_fixture(Path.join(__DIR__, ".."), "formatter", fn ->
path = "lib/file.ex"
Expand Down Expand Up @@ -161,6 +165,7 @@ defmodule ElixirLS.LanguageServer.Providers.FormattingTest do
end)
end

@tag :fixture
test "Proper utf-16 format: zalgo" do
in_fixture(Path.join(__DIR__, ".."), "formatter", fn ->
path = "lib/file.ex"
Expand Down
Loading