Skip to content

Commit de4e4eb

Browse files
author
Étienne Lévesque
authored
fix(test runner): Use ExUnit testPaths and testPattern (#500)
* fix(test runner): Use ExUnit testPaths and testPattern * Handle bad URI format * Add tests for test_paths and test_pattern * Make all fixture apps unique * Rename umbrella app to match filesystem * wait for compilation after tests * wait for compilation before code lens request
1 parent 70349fe commit de4e4eb

File tree

9 files changed

+256
-35
lines changed

9 files changed

+256
-35
lines changed

apps/language_server/lib/language_server/providers/code_lens/test.ex

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
7979
runnable_functions = [{:test, 3}, {:test, 2}]
8080

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

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

104-
for {line, _col} <- calls_to(calls_list, {:describe, 2}),
105-
is_test_module?(lines_to_env_list, line) do
103+
for {line, _col} <- calls_to(calls_list, {:describe, 2}) do
106104
DescribeBlock.find_block_info(
107105
line,
108106
lines_to_env_list,
@@ -126,23 +124,9 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
126124
defp get_test_modules(lines_to_env) do
127125
lines_to_env
128126
|> Enum.group_by(fn {_line, env} -> env.module end)
129-
|> Enum.filter(fn {_module, module_lines_to_env} -> is_test_module?(module_lines_to_env) end)
130127
|> Enum.map(fn {module, [{line, _env} | _rest]} -> {module, line} end)
131128
end
132129

133-
defp is_test_module?(lines_to_env), do: is_test_module?(lines_to_env, :infinity)
134-
135-
defp is_test_module?(lines_to_env, line) when is_list(lines_to_env) do
136-
lines_to_env
137-
|> Enum.max_by(fn
138-
{env_line, _env} when env_line < line -> env_line
139-
_ -> -1
140-
end)
141-
|> elem(1)
142-
|> Map.get(:imports)
143-
|> Enum.any?(fn module -> module == ExUnit.Case end)
144-
end
145-
146130
defp calls_to(calls_list, {function, arity}) do
147131
for call_info <- calls_list,
148132
call_info.func == function and call_info.arity === arity do

apps/language_server/lib/language_server/server.ex

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -822,13 +822,71 @@ defmodule ElixirLS.LanguageServer.Server do
822822
end
823823

824824
defp get_test_code_lenses(state, uri, source_file) do
825-
if state.settings["enableTestLenses"] == true do
826-
CodeLens.test_code_lens(uri, source_file.text, state.project_dir)
827-
else
828-
{:ok, []}
825+
get_test_code_lenses(
826+
state,
827+
uri,
828+
source_file,
829+
state.settings["enableTestLenses"] || false,
830+
Mix.Project.umbrella?()
831+
)
832+
end
833+
834+
defp get_test_code_lenses(_state, _uri, _source_file, false, _), do: {:ok, []}
835+
836+
defp get_test_code_lenses(state, uri, source_file, true = _enabled, true = _umbrella) do
837+
file_path = SourceFile.path_from_uri(uri)
838+
839+
Mix.Project.apps_paths()
840+
|> Enum.find(fn {_app, app_path} -> String.contains?(file_path, app_path) end)
841+
|> case do
842+
nil ->
843+
{:ok, []}
844+
845+
{app, app_path} ->
846+
if is_test_file?(file_path, state, app, app_path) do
847+
CodeLens.test_code_lens(uri, source_file.text, "#{state.project_dir}/#{app_path}")
848+
else
849+
{:ok, []}
850+
end
851+
end
852+
end
853+
854+
defp get_test_code_lenses(state, uri, source_file, true = _enabled, false = _umbrella) do
855+
try do
856+
file_path = SourceFile.path_from_uri(uri)
857+
858+
if is_test_file?(file_path) do
859+
CodeLens.test_code_lens(uri, source_file.text, state.project_dir)
860+
else
861+
{:ok, []}
862+
end
863+
rescue
864+
_ in ArgumentError -> {:ok, []}
829865
end
830866
end
831867

868+
defp is_test_file?(file_path, state, app, app_path) do
869+
app_name = Atom.to_string(app)
870+
871+
test_paths =
872+
(get_in(state.settings, ["testPaths", app_name]) || ["test"])
873+
|> Enum.map(fn path -> Path.join([state.project_dir, app_path, path]) end)
874+
875+
test_pattern = get_in(state.settings, ["testPattern", app_name]) || "*_test.exs"
876+
877+
Mix.Utils.extract_files(test_paths, test_pattern)
878+
|> Enum.any?(fn path -> String.ends_with?(file_path, path) end)
879+
end
880+
881+
defp is_test_file?(file_path) do
882+
test_paths = Mix.Project.config()[:test_paths] || ["test"]
883+
test_pattern = Mix.Project.config()[:test_pattern] || "*_test.exs"
884+
885+
Mix.Utils.extract_files(test_paths, test_pattern)
886+
|> Enum.map(&Path.absname/1)
887+
|> Enum.any?(&(&1 == file_path))
888+
end
889+
832890
# Build
833891

834892
defp trigger_build(state) do
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defmodule TestCodeLensCustomPathsAndPatternTest do
2+
use ExUnit.Case
3+
4+
test "fixture test" do
5+
assert true
6+
end
7+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule TestCodeLensCustomPathsAndPattern.MixProject do
2+
use Mix.Project
3+
4+
def project do
5+
[
6+
app: :test_code_lens_custom_paths_and_pattern,
7+
version: "0.1.0",
8+
test_paths: ["custom_path"],
9+
test_pattern: "*_custom_test.exs"
10+
]
11+
end
12+
13+
def application, do: []
14+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defmodule UmbrellaTestCodeLensCustomPathAndPatternTest do
2+
use ExUnit.Case
3+
4+
test "fixture test" do
5+
assert true
6+
end
7+
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
defmodule App1.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[
6+
app: :app1,
7+
version: "0.1.0"
8+
]
9+
end
10+
11+
def application do
12+
[]
13+
end
14+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defmodule UmbrellaTestCodeLensCustomPathAndPattern.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[apps_path: "apps"]
6+
end
7+
end

apps/language_server/test/providers/code_lens/test_test.exs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,6 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.TestTest do
7070
]
7171
end
7272

73-
test "does not return lenses for modules that don't import ExUnit.case" do
74-
uri = "file:///project/file.ex"
75-
76-
text = """
77-
defmodule MyModule do
78-
end
79-
"""
80-
81-
{:ok, lenses} = CodeLens.Test.code_lens(uri, text, @project_dir)
82-
83-
assert lenses == []
84-
end
85-
8673
test "returns lenses for all describe blocks" do
8774
uri = "file:///project/file.ex"
8875

apps/language_server/test/server_test.exs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,149 @@ defmodule ElixirLS.LanguageServer.ServerTest do
12791279
end)
12801280
end
12811281

1282+
@tag :fixture
1283+
test "returns code lenses for runnable tests with custom test paths and test pattern", %{
1284+
server: server
1285+
} do
1286+
in_fixture(__DIR__, "test_code_lens_custom_paths_and_pattern", fn ->
1287+
file_path = "custom_path/fixture_custom_test.exs"
1288+
file_uri = SourceFile.path_to_uri(file_path)
1289+
file_absolute_path = SourceFile.path_from_uri(file_uri)
1290+
text = File.read!(file_path)
1291+
project_dir = SourceFile.path_from_uri(root_uri())
1292+
1293+
initialize(server)
1294+
1295+
Server.receive_packet(
1296+
server,
1297+
did_change_configuration(%{"elixirLS" => %{"enableTestLenses" => true}})
1298+
)
1299+
1300+
Server.receive_packet(server, did_open(file_uri, "elixir", 1, text))
1301+
1302+
wait_until_compiled(server)
1303+
1304+
Server.receive_packet(
1305+
server,
1306+
code_lens_req(4, file_uri)
1307+
)
1308+
1309+
resp = assert_receive(%{"id" => 4}, 5000)
1310+
1311+
assert response(4, [
1312+
%{
1313+
"command" => %{
1314+
"arguments" => [
1315+
%{
1316+
"filePath" => ^file_absolute_path,
1317+
"testName" => "fixture test",
1318+
"projectDir" => ^project_dir
1319+
}
1320+
],
1321+
"command" => "elixir.lens.test.run",
1322+
"title" => "Run test"
1323+
},
1324+
"range" => %{
1325+
"end" => %{"character" => 0, "line" => 3},
1326+
"start" => %{"character" => 0, "line" => 3}
1327+
}
1328+
},
1329+
%{
1330+
"command" => %{
1331+
"arguments" => [
1332+
%{
1333+
"filePath" => ^file_absolute_path,
1334+
"module" => "Elixir.TestCodeLensCustomPathsAndPatternTest",
1335+
"projectDir" => ^project_dir
1336+
}
1337+
],
1338+
"command" => "elixir.lens.test.run",
1339+
"title" => "Run tests in module"
1340+
},
1341+
"range" => %{
1342+
"end" => %{"character" => 0, "line" => 0},
1343+
"start" => %{"character" => 0, "line" => 0}
1344+
}
1345+
}
1346+
]) = resp
1347+
end)
1348+
end
1349+
1350+
@tag :fixture
1351+
test "returns code lenses for runnable tests with custom test paths and test pattern in umbrella apps",
1352+
%{
1353+
server: server
1354+
} do
1355+
in_fixture(__DIR__, "umbrella_test_code_lens_custom_path_and_pattern", fn ->
1356+
file_path = "apps/app1/custom_path/fixture_custom_test.exs"
1357+
file_uri = SourceFile.path_to_uri(file_path)
1358+
file_absolute_path = SourceFile.path_from_uri(file_uri)
1359+
text = File.read!(file_path)
1360+
project_dir = SourceFile.path_from_uri("#{root_uri()}/apps/app1")
1361+
1362+
initialize(server)
1363+
1364+
Server.receive_packet(
1365+
server,
1366+
did_change_configuration(%{
1367+
"elixirLS" => %{
1368+
"enableTestLenses" => true,
1369+
"testPaths" => %{"app1" => ["custom_path"]},
1370+
"testPattern" => %{"app1" => "*_custom_test.exs"}
1371+
}
1372+
})
1373+
)
1374+
1375+
Server.receive_packet(server, did_open(file_uri, "elixir", 1, text))
1376+
1377+
wait_until_compiled(server)
1378+
1379+
Server.receive_packet(
1380+
server,
1381+
code_lens_req(4, file_uri)
1382+
)
1383+
1384+
resp = assert_receive(%{"id" => 4}, 5000)
1385+
1386+
assert response(4, [
1387+
%{
1388+
"command" => %{
1389+
"arguments" => [
1390+
%{
1391+
"filePath" => ^file_absolute_path,
1392+
"testName" => "fixture test",
1393+
"projectDir" => ^project_dir
1394+
}
1395+
],
1396+
"command" => "elixir.lens.test.run",
1397+
"title" => "Run test"
1398+
},
1399+
"range" => %{
1400+
"end" => %{"character" => 0, "line" => 3},
1401+
"start" => %{"character" => 0, "line" => 3}
1402+
}
1403+
},
1404+
%{
1405+
"command" => %{
1406+
"arguments" => [
1407+
%{
1408+
"filePath" => ^file_absolute_path,
1409+
"module" => "Elixir.UmbrellaTestCodeLensCustomPathAndPatternTest",
1410+
"projectDir" => ^project_dir
1411+
}
1412+
],
1413+
"command" => "elixir.lens.test.run",
1414+
"title" => "Run tests in module"
1415+
},
1416+
"range" => %{
1417+
"end" => %{"character" => 0, "line" => 0},
1418+
"start" => %{"character" => 0, "line" => 0}
1419+
}
1420+
}
1421+
]) = resp
1422+
end)
1423+
end
1424+
12821425
defp with_new_server(func) do
12831426
server = start_supervised!({Server, nil})
12841427
packet_capture = start_supervised!({PacketCapture, self()})

0 commit comments

Comments
 (0)