Skip to content
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

fix(test runner): Use ExUnit testPaths and testPattern #500

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
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
runnable_functions = [{:test, 3}, {:test, 2}]

for func <- runnable_functions,
{line, _col} <- calls_to(calls_list, func),
is_test_module?(lines_to_env_list, line) do
{line, _col} <- calls_to(calls_list, func) do
{_line, %{scope_id: scope_id}} =
Enum.find(lines_to_env_list, fn {env_line, _env} -> env_line == line end)

Expand All @@ -101,8 +100,7 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
defp find_describe_blocks(lines_to_env_list, calls_list, source_lines) do
lines_to_env_list_length = length(lines_to_env_list)

for {line, _col} <- calls_to(calls_list, {:describe, 2}),
is_test_module?(lines_to_env_list, line) do
for {line, _col} <- calls_to(calls_list, {:describe, 2}) do
DescribeBlock.find_block_info(
line,
lines_to_env_list,
Expand All @@ -126,23 +124,9 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
defp get_test_modules(lines_to_env) do
lines_to_env
|> Enum.group_by(fn {_line, env} -> env.module end)
|> Enum.filter(fn {_module, module_lines_to_env} -> is_test_module?(module_lines_to_env) end)
|> Enum.map(fn {module, [{line, _env} | _rest]} -> {module, line} end)
end

defp is_test_module?(lines_to_env), do: is_test_module?(lines_to_env, :infinity)

defp is_test_module?(lines_to_env, line) when is_list(lines_to_env) do
lines_to_env
|> Enum.max_by(fn
{env_line, _env} when env_line < line -> env_line
_ -> -1
end)
|> elem(1)
|> Map.get(:imports)
|> Enum.any?(fn module -> module == ExUnit.Case end)
end

defp calls_to(calls_list, {function, arity}) do
for call_info <- calls_list,
call_info.func == function and call_info.arity === arity do
Expand Down
66 changes: 62 additions & 4 deletions apps/language_server/lib/language_server/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -818,13 +818,71 @@ defmodule ElixirLS.LanguageServer.Server do
end

defp get_test_code_lenses(state, uri, source_file) do
if state.settings["enableTestLenses"] == true do
CodeLens.test_code_lens(uri, source_file.text, state.project_dir)
else
{:ok, []}
get_test_code_lenses(
state,
uri,
source_file,
state.settings["enableTestLenses"] || false,
Mix.Project.umbrella?()
)
end

defp get_test_code_lenses(_state, _uri, _source_file, false, _), do: {:ok, []}

defp get_test_code_lenses(state, uri, source_file, true = _enabled, true = _umbrella) do
file_path = SourceFile.path_from_uri(uri)

Mix.Project.apps_paths()
|> Enum.find(fn {_app, app_path} -> String.contains?(file_path, app_path) end)
|> case do
nil ->
{:ok, []}

{app, app_path} ->
if is_test_file?(file_path, state, app, app_path) do
CodeLens.test_code_lens(uri, source_file.text, "#{state.project_dir}/#{app_path}")
else
{:ok, []}
end
end
end

defp get_test_code_lenses(state, uri, source_file, true = _enabled, false = _umbrella) do
try do
file_path = SourceFile.path_from_uri(uri)

if is_test_file?(file_path) do
CodeLens.test_code_lens(uri, source_file.text, state.project_dir)
else
{:ok, []}
end
rescue
_ in ArgumentError -> {:ok, []}
end
end

defp is_test_file?(file_path, state, app, app_path) do
app_name = Atom.to_string(app)

test_paths =
(get_in(state.settings, ["testPaths", app_name]) || ["test"])
|> Enum.map(fn path -> Path.join([state.project_dir, app_path, path]) end)

test_pattern = get_in(state.settings, ["testPattern", app_name]) || "*_test.exs"

Mix.Utils.extract_files(test_paths, test_pattern)
|> Enum.any?(fn path -> String.ends_with?(file_path, path) end)
end

defp is_test_file?(file_path) do
test_paths = Mix.Project.config()[:test_paths] || ["test"]
test_pattern = Mix.Project.config()[:test_pattern] || "*_test.exs"

Mix.Utils.extract_files(test_paths, test_pattern)
|> Enum.map(&Path.absname/1)
|> Enum.any?(&(&1 == file_path))
end

# Build

defp trigger_build(state) do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule TestCodeLensCustomPathsAndPatternTest do
use ExUnit.Case

test "fixture test" do
assert true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule TestCodeLensCustomPathsAndPattern.MixProject do
use Mix.Project

def project do
[
app: :test_code_lens_custom_paths_and_pattern,
version: "0.1.0",
test_paths: ["custom_path"],
test_pattern: "*_custom_test.exs"
]
end

def application, do: []
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule UmbrellaTestCodeLensCustomPathAndPatternTest do
use ExUnit.Case

test "fixture test" do
assert true
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule App1.Mixfile do
use Mix.Project

def project do
[
app: :app1,
version: "0.1.0"
]
end

def application do
[]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
defmodule UmbrellaTestCodeLensCustomPathAndPattern.Mixfile do
use Mix.Project

def project do
[apps_path: "apps"]
end
end
13 changes: 0 additions & 13 deletions apps/language_server/test/providers/code_lens/test_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,6 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.TestTest do
]
end

test "does not return lenses for modules that don't import ExUnit.case" do
uri = "file:///project/file.ex"

text = """
defmodule MyModule do
end
"""

{:ok, lenses} = CodeLens.Test.code_lens(uri, text, @project_dir)

assert lenses == []
end

test "returns lenses for all describe blocks" do
uri = "file:///project/file.ex"

Expand Down
143 changes: 143 additions & 0 deletions apps/language_server/test/server_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,149 @@ defmodule ElixirLS.LanguageServer.ServerTest do
end)
end

@tag :fixture
test "returns code lenses for runnable tests with custom test paths and test pattern", %{
server: server
} do
in_fixture(__DIR__, "test_code_lens_custom_paths_and_pattern", fn ->
file_path = "custom_path/fixture_custom_test.exs"
file_uri = SourceFile.path_to_uri(file_path)
file_absolute_path = SourceFile.path_from_uri(file_uri)
text = File.read!(file_path)
project_dir = SourceFile.path_from_uri(root_uri())

initialize(server)

Server.receive_packet(
server,
did_change_configuration(%{"elixirLS" => %{"enableTestLenses" => true}})
)

Server.receive_packet(server, did_open(file_uri, "elixir", 1, text))

wait_until_compiled(server)

Server.receive_packet(
server,
code_lens_req(4, file_uri)
)

resp = assert_receive(%{"id" => 4}, 5000)

assert response(4, [
%{
"command" => %{
"arguments" => [
%{
"filePath" => ^file_absolute_path,
"testName" => "fixture test",
"projectDir" => ^project_dir
}
],
"command" => "elixir.lens.test.run",
"title" => "Run test"
},
"range" => %{
"end" => %{"character" => 0, "line" => 3},
"start" => %{"character" => 0, "line" => 3}
}
},
%{
"command" => %{
"arguments" => [
%{
"filePath" => ^file_absolute_path,
"module" => "Elixir.TestCodeLensCustomPathsAndPatternTest",
"projectDir" => ^project_dir
}
],
"command" => "elixir.lens.test.run",
"title" => "Run tests in module"
},
"range" => %{
"end" => %{"character" => 0, "line" => 0},
"start" => %{"character" => 0, "line" => 0}
}
}
]) = resp
end)
end

@tag :fixture
test "returns code lenses for runnable tests with custom test paths and test pattern in umbrella apps",
%{
server: server
} do
in_fixture(__DIR__, "umbrella_test_code_lens_custom_path_and_pattern", fn ->
file_path = "apps/app1/custom_path/fixture_custom_test.exs"
file_uri = SourceFile.path_to_uri(file_path)
file_absolute_path = SourceFile.path_from_uri(file_uri)
text = File.read!(file_path)
project_dir = SourceFile.path_from_uri("#{root_uri()}/apps/app1")

initialize(server)

Server.receive_packet(
server,
did_change_configuration(%{
"elixirLS" => %{
"enableTestLenses" => true,
"testPaths" => %{"app1" => ["custom_path"]},
"testPattern" => %{"app1" => "*_custom_test.exs"}
}
})
)

Server.receive_packet(server, did_open(file_uri, "elixir", 1, text))

wait_until_compiled(server)

Server.receive_packet(
server,
code_lens_req(4, file_uri)
)

resp = assert_receive(%{"id" => 4}, 5000)

assert response(4, [
%{
"command" => %{
"arguments" => [
%{
"filePath" => ^file_absolute_path,
"testName" => "fixture test",
"projectDir" => ^project_dir
}
],
"command" => "elixir.lens.test.run",
"title" => "Run test"
},
"range" => %{
"end" => %{"character" => 0, "line" => 3},
"start" => %{"character" => 0, "line" => 3}
}
},
%{
"command" => %{
"arguments" => [
%{
"filePath" => ^file_absolute_path,
"module" => "Elixir.UmbrellaTestCodeLensCustomPathAndPatternTest",
"projectDir" => ^project_dir
}
],
"command" => "elixir.lens.test.run",
"title" => "Run tests in module"
},
"range" => %{
"end" => %{"character" => 0, "line" => 0},
"start" => %{"character" => 0, "line" => 0}
}
}
]) = resp
end)
end

defp with_new_server(func) do
server = start_supervised!({Server, nil})
packet_capture = start_supervised!({PacketCapture, self()})
Expand Down