Skip to content

Commit afa71aa

Browse files
committed
Test pretty printing types in elixir
1 parent 44b474d commit afa71aa

File tree

3 files changed

+151
-15
lines changed

3 files changed

+151
-15
lines changed
104 Bytes
Binary file not shown.

test/examples/type/wrong_ret.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ defmodule WrongRet do
88
@spec ret_wrong_atom3() :: atom()
99
def ret_wrong_atom3, do: %{a: 1}
1010

11-
@spec ret_wrong_atom4() :: atom()
12-
def ret_wrong_atom4, do: false
11+
# @spec ret_wrong_atom4() :: atom()
12+
# def ret_wrong_atom4, do: false
1313

1414
@spec ret_wrong_integer() :: integer()
1515
def ret_wrong_integer, do: 1.0
@@ -46,4 +46,7 @@ defmodule WrongRet do
4646

4747
@spec ret_wrong_map() :: map()
4848
def ret_wrong_map, do: {:a, 1, 2}
49+
50+
@spec ret_wrong_fun() :: (... -> atom())
51+
def ret_wrong_fun, do: fn -> 12 end
4952
end

test/gradient/elixir_fmt_test.exs

Lines changed: 146 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ defmodule Gradient.ElixirFmtTest do
88

99
@example_module_path "test/examples/simple_app.ex"
1010

11+
setup_all config do
12+
load_wrong_ret_error_examples(config)
13+
end
14+
1115
test "try_highlight_in_context/2" do
1216
opts = [forms: basic_erlang_forms()]
1317
expression = {:integer, 31, 12}
@@ -20,16 +24,110 @@ defmodule Gradient.ElixirFmtTest do
2024
assert res == expected
2125
end
2226

23-
@tag :skip
2427
describe "types format" do
25-
test "wrong return type" do
26-
{_tokens, ast} = load("/type/Elixir.WrongRet.beam", "/type/wrong_ret.ex")
27-
opts = []
28-
errors = type_check_file(ast, opts)
28+
test "return integer() instead atom()", %{wrong_ret_errors: errors} do
29+
msg = format_error_to_binary(errors.ret_wrong_atom)
30+
31+
assert String.contains?(msg, "atom()")
32+
assert String.contains?(msg, "1")
33+
end
34+
35+
test "return tuple() instead atom()", %{wrong_ret_errors: errors} do
36+
msg = format_error_to_binary(errors.ret_wrong_atom2)
37+
38+
assert String.contains?(msg, "atom()")
39+
assert String.contains?(msg, "{:ok, []}")
40+
end
41+
42+
test "return map() instead atom()", %{wrong_ret_errors: errors} do
43+
msg = format_error_to_binary(errors.ret_wrong_atom3)
44+
45+
assert String.contains?(msg, "atom()")
46+
assert String.contains?(msg, "%{required(:a) => 1}")
47+
end
48+
49+
test "return float() instead integer()", %{wrong_ret_errors: errors} do
50+
msg = format_error_to_binary(errors.ret_wrong_integer)
51+
52+
assert String.contains?(msg, "integer()")
53+
assert String.contains?(msg, "1.0")
54+
end
55+
56+
test "return atom() instead integer()", %{wrong_ret_errors: errors} do
57+
msg = format_error_to_binary(errors.ret_wrong_integer2)
58+
59+
assert String.contains?(msg, "integer()")
60+
assert String.contains?(msg, ":ok")
61+
end
62+
63+
test "return boolean() instead integer()", %{wrong_ret_errors: errors} do
64+
msg = format_error_to_binary(errors.ret_wrong_integer3)
65+
66+
assert String.contains?(msg, "integer()")
67+
assert String.contains?(msg, "true")
68+
end
69+
70+
test "return list() instead integer()", %{wrong_ret_errors: errors} do
71+
msg = format_error_to_binary(errors.ret_wrong_integer4)
72+
73+
assert String.contains?(msg, "integer()")
74+
assert String.contains?(msg, "nonempty_list()")
75+
end
76+
77+
test "return integer() out of the range()", %{wrong_ret_errors: errors} do
78+
msg = format_error_to_binary(errors.ret_out_of_range_int)
79+
80+
assert String.contains?(msg, "range(1, 10)")
81+
assert String.contains?(msg, "12")
82+
end
83+
84+
test "return atom() instead boolean()", %{wrong_ret_errors: errors} do
85+
msg = format_error_to_binary(errors.ret_wrong_boolean)
86+
87+
assert String.contains?(msg, "boolean()")
88+
assert String.contains?(msg, ":ok")
89+
end
90+
91+
test "return binary() instead boolean()", %{wrong_ret_errors: errors} do
92+
msg = format_error_to_binary(errors.ret_wrong_boolean2)
93+
94+
assert String.contains?(msg, "boolean()")
95+
assert String.contains?(msg, "binary()")
96+
end
97+
98+
test "return integer() instead boolean()", %{wrong_ret_errors: errors} do
99+
msg = format_error_to_binary(errors.ret_wrong_boolean3)
100+
101+
assert String.contains?(msg, "boolean()")
102+
assert String.contains?(msg, "1")
103+
end
104+
105+
test "return keyword() instead boolean()", %{wrong_ret_errors: errors} do
106+
msg = format_error_to_binary(errors.ret_wrong_boolean4)
107+
108+
assert String.contains?(msg, "boolean()")
109+
assert String.contains?(msg, "nonempty_list()")
110+
end
111+
112+
test "return list() instead keyword()", %{wrong_ret_errors: errors} do
113+
msg = format_error_to_binary(errors.ret_wrong_keyword)
114+
115+
assert String.contains?(msg, "{atom(), any()}")
116+
assert String.contains?(msg, "1")
117+
end
118+
119+
test "return tuple() instead map()", %{wrong_ret_errors: errors} do
120+
msg = format_error_to_binary(errors.ret_wrong_map)
29121

30-
for e <- errors do
31-
:io.put_chars(e)
32-
end
122+
assert String.contains?(msg, "map()")
123+
assert String.contains?(msg, "{:a, 1, 2}")
124+
end
125+
126+
test "return lambda with wrong returned type", %{wrong_ret_errors: errors} do
127+
msg = format_error_to_binary(errors.ret_wrong_fun)
128+
129+
assert String.contains?(msg, "atom()")
130+
assert String.contains?(msg, "12")
33131
end
34132
end
35133

@@ -44,17 +142,52 @@ defmodule Gradient.ElixirFmtTest do
44142
IO.puts(res)
45143
end
46144

47-
def basic_erlang_forms() do
145+
# Helpers
146+
147+
defp basic_erlang_forms() do
48148
[{:attribute, 1, :file, {@example_module_path, 1}}]
49149
end
50150

51-
def type_check_file(ast, opts) do
151+
defp type_check_file(ast, opts) do
52152
forms = AstSpecifier.specify(ast)
53153
opts = Keyword.put(opts, :return_errors, true)
54154
opts = Keyword.put(opts, :forms, forms)
55155

56-
forms
57-
|> :gradualizer.type_check_forms(opts)
58-
|> Enum.map(fn {_, err} -> ElixirFmt.format_error(err, opts) end)
156+
errors =
157+
forms
158+
|> :gradualizer.type_check_forms(opts)
159+
|> Enum.map(&elem(&1, 1))
160+
161+
{errors, forms}
162+
end
163+
164+
defp format_error_to_binary(error, opts \\ []) do
165+
error
166+
|> ElixirFmt.format_error(opts)
167+
|> :erlang.iolist_to_binary()
168+
end
169+
170+
@spec load_wrong_ret_error_examples(map()) :: map()
171+
defp load_wrong_ret_error_examples(config) do
172+
{_tokens, ast} = load("/type/Elixir.WrongRet.beam", "/type/wrong_ret.ex")
173+
174+
{errors, forms} = type_check_file(ast, [])
175+
names = get_function_names_from_ast(forms)
176+
177+
errors_map =
178+
Enum.zip(names, errors)
179+
|> Map.new()
180+
181+
Map.put(config, :wrong_ret_errors, errors_map)
182+
end
183+
184+
@spec get_function_names_from_ast([tuple()]) :: [atom()]
185+
def get_function_names_from_ast(ast) do
186+
ast
187+
|> Enum.filter(fn
188+
{:function, _, name, _, _} -> name != :__info__
189+
_ -> false
190+
end)
191+
|> Enum.map(&elem(&1, 2))
59192
end
60193
end

0 commit comments

Comments
 (0)