Skip to content

Commit 848f57b

Browse files
committed
Unit tests
1 parent 635b77e commit 848f57b

File tree

4 files changed

+159
-62
lines changed

4 files changed

+159
-62
lines changed

lib/elixir/lib/protocol.ex

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -612,9 +612,9 @@ defmodule Protocol do
612612
protocol_def = change_protocol(protocol_def, types)
613613
impl_for = change_impl_for(impl_for, protocol, types)
614614
struct_impl_for = change_struct_impl_for(struct_impl_for, protocol, types, structs)
615-
definitions = [protocol_def, impl_for, impl_for!, struct_impl_for] ++ definitions
615+
new_signatures = new_signatures(definitions, protocol, types)
616616

617-
new_signatures = new_signatures(definitions, protocol, types, structs)
617+
definitions = [protocol_def, impl_for, impl_for!, struct_impl_for] ++ definitions
618618
signatures = Enum.into(new_signatures, signatures)
619619
{:ok, definitions, signatures}
620620

@@ -623,34 +623,41 @@ defmodule Protocol do
623623
end
624624
end
625625

626-
defp new_signatures(definitions, protocol, types, structs) do
626+
defp new_signatures(definitions, protocol, types) do
627627
alias Module.Types.Descr
628628

629629
clauses =
630-
Enum.map(structs ++ List.delete(types, Any), fn impl ->
630+
types
631+
|> List.delete(Any)
632+
|> Enum.map(fn impl ->
631633
{[Module.Types.Of.impl(impl)], Descr.atom([Module.concat(protocol, impl)])}
632634
end)
633635

634-
# TODO: Test a protocol with no implementation
635-
domain =
636+
{domain, impl_for, impl_for!} =
636637
case clauses do
637638
[] ->
638-
Descr.none()
639+
if Any in types do
640+
clauses = [{[Descr.term()], Descr.atom([Module.concat(protocol, Any)])}]
641+
{Descr.none(), clauses, clauses}
642+
else
643+
{Descr.none(), [{[Descr.term()], Descr.atom([nil])}],
644+
[{[Descr.none()], Descr.none()}]}
645+
end
639646

640647
_ ->
641-
clauses
642-
|> Enum.map(fn {[domain], _} -> domain end)
643-
|> Enum.reduce(&Descr.union/2)
644-
end
645-
646-
not_domain = Descr.negation(domain)
647-
648-
{domain, impl_for, impl_for!} =
649-
if Any in types do
650-
clauses = clauses ++ [{[not_domain], Descr.atom([Module.concat(protocol, Any)])}]
651-
{Descr.term(), clauses, clauses}
652-
else
653-
{domain, clauses ++ [{[not_domain], Descr.atom([nil])}], clauses}
648+
domain =
649+
clauses
650+
|> Enum.map(fn {[domain], _} -> domain end)
651+
|> Enum.reduce(&Descr.union/2)
652+
653+
not_domain = Descr.negation(domain)
654+
655+
if Any in types do
656+
clauses = clauses ++ [{[not_domain], Descr.atom([Module.concat(protocol, Any)])}]
657+
{Descr.term(), clauses, clauses}
658+
else
659+
{domain, clauses ++ [{[not_domain], Descr.atom([nil])}], clauses}
660+
end
654661
end
655662

656663
new_signatures =
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
defprotocol Protocol.ConsolidationTest.NoImpl do
2+
def ok(term)
3+
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defprotocol Protocol.ConsolidationTest.WithAny do
22
@fallback_to_any true
33
@doc "Ok"
4-
def ok(term)
4+
def ok(term, opts)
55
end

lib/elixir/test/elixir/protocol/consolidation_test.exs

Lines changed: 128 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ Kernel.ParallelCompiler.compile_to_path(files, path, return_diagnostics: true)
88

99
defmodule Protocol.ConsolidationTest do
1010
use ExUnit.Case, async: true
11-
alias Protocol.ConsolidationTest.{Sample, WithAny}
11+
alias Protocol.ConsolidationTest.{Sample, WithAny, NoImpl}
1212

1313
defimpl WithAny, for: Map do
14-
def ok(map) do
14+
def ok(map, _opts) do
1515
{:ok, map}
1616
end
1717
end
1818

1919
defimpl WithAny, for: Any do
20-
def ok(any) do
20+
def ok(any, _opts) do
2121
{:ok, any}
2222
end
2323
end
@@ -47,14 +47,24 @@ defmodule Protocol.ConsolidationTest do
4747
{:ok, binary} = Protocol.consolidate(Sample, [Any, ImplStruct])
4848
:code.load_binary(Sample, ~c"protocol_test.exs", binary)
4949

50-
@sample_binary binary
50+
defp sample_binary, do: unquote(binary)
5151

5252
# Any should be moved to the end
5353
:code.purge(WithAny)
5454
:code.delete(WithAny)
5555
{:ok, binary} = Protocol.consolidate(WithAny, [Any, ImplStruct, Map])
5656
:code.load_binary(WithAny, ~c"protocol_test.exs", binary)
5757

58+
defp with_any_binary, do: unquote(binary)
59+
60+
# No Any
61+
:code.purge(NoImpl)
62+
:code.delete(NoImpl)
63+
{:ok, binary} = Protocol.consolidate(NoImpl, [])
64+
:code.load_binary(NoImpl, ~c"protocol_test.exs", binary)
65+
66+
defp no_impl_binary, do: unquote(binary)
67+
5868
test "consolidated?/1" do
5969
assert Protocol.consolidated?(WithAny)
6070
refute Protocol.consolidated?(Enumerable)
@@ -64,7 +74,7 @@ defmodule Protocol.ConsolidationTest do
6474
output =
6575
ExUnit.CaptureIO.capture_io(:stderr, fn ->
6676
defimpl WithAny, for: Integer do
67-
def ok(_any), do: :ok
77+
def ok(_any, _opts), do: :ok
6878
end
6979
end)
7080

@@ -86,7 +96,7 @@ defmodule Protocol.ConsolidationTest do
8696
:code.delete(WithAny.Integer)
8797
end
8898

89-
test "consolidated implementations without any" do
99+
test "consolidated implementations without fallback to any" do
90100
assert is_nil(Sample.impl_for(:foo))
91101
assert is_nil(Sample.impl_for(fn x -> x end))
92102
assert is_nil(Sample.impl_for(1))
@@ -106,8 +116,9 @@ defmodule Protocol.ConsolidationTest do
106116
assert Sample.impl_for(%NoImplStruct{}) == nil
107117
end
108118

109-
test "consolidated implementations with any and tuple fallback" do
119+
test "consolidated implementations with fallback to any" do
110120
assert WithAny.impl_for(%NoImplStruct{}) == WithAny.Any
121+
111122
# Derived
112123
assert WithAny.impl_for(%ImplStruct{}) ==
113124
Protocol.ConsolidationTest.WithAny.Protocol.ConsolidationTest.ImplStruct
@@ -118,66 +129,120 @@ defmodule Protocol.ConsolidationTest do
118129
end
119130

120131
test "consolidation keeps docs" do
121-
{:ok, {Sample, [{~c"Docs", docs_bin}]}} = :beam_lib.chunks(@sample_binary, [~c"Docs"])
132+
{:ok, {Sample, [{~c"Docs", docs_bin}]}} = :beam_lib.chunks(sample_binary(), [~c"Docs"])
122133
{:docs_v1, _, _, _, _, _, docs} = :erlang.binary_to_term(docs_bin)
123134
ok_doc = List.keyfind(docs, {:function, :ok, 1}, 0)
124135

125136
assert {{:function, :ok, 1}, _, ["ok(term)"], %{"en" => "Ok"}, _} = ok_doc
126137
end
127138

128-
test "consolidation keeps chunks" do
129-
deprecated = [{{:ok, 1}, "Reason"}]
130-
assert deprecated == Sample.__info__(:deprecated)
131-
132-
{:ok, {Sample, [{~c"ExCk", check_bin}]}} = :beam_lib.chunks(@sample_binary, [~c"ExCk"])
133-
assert {:elixir_checker_v1, contents} = :erlang.binary_to_term(check_bin)
134-
assert %{{:ok, 1} => %{deprecated: "Reason", sig: _}} = Map.new(contents.exports)
135-
end
136-
137139
@tag :requires_source
138140
test "consolidation keeps source" do
139141
assert Sample.__info__(:compile)[:source]
140142
end
141143

142144
test "consolidated keeps callbacks" do
143-
{:ok, callbacks} = Code.Typespec.fetch_callbacks(@sample_binary)
145+
{:ok, callbacks} = Code.Typespec.fetch_callbacks(sample_binary())
144146
assert callbacks != []
145147
end
146148

147-
test "consolidation errors on missing BEAM files" do
148-
defprotocol NoBeam do
149-
def example(arg)
150-
end
151-
152-
assert Protocol.consolidate(String, []) == {:error, :not_a_protocol}
153-
assert Protocol.consolidate(NoBeam, []) == {:error, :no_beam_info}
154-
end
155-
156149
test "consolidation updates attributes" do
157150
assert Sample.__protocol__(:consolidated?)
158151
assert Sample.__protocol__(:impls) == {:consolidated, [ImplStruct]}
159152
assert WithAny.__protocol__(:consolidated?)
160153
assert WithAny.__protocol__(:impls) == {:consolidated, [Any, Map, ImplStruct]}
154+
assert NoImpl.__protocol__(:consolidated?)
155+
assert NoImpl.__protocol__(:impls) == {:consolidated, []}
161156
end
162157

163-
test "consolidation extracts protocols" do
164-
protos = Protocol.extract_protocols([Application.app_dir(:elixir, "ebin")])
165-
assert Enumerable in protos
166-
assert Inspect in protos
167-
end
158+
describe "exports" do
159+
import Module.Types.Descr
160+
alias Module.Types.Of
161+
162+
defp exports(binary) do
163+
{:ok, {_, [{~c"ExCk", check_bin}]}} = :beam_lib.chunks(binary, [~c"ExCk"])
164+
assert {:elixir_checker_v1, contents} = :erlang.binary_to_term(check_bin)
165+
Map.new(contents.exports)
166+
end
167+
168+
test "keeps deprecations" do
169+
deprecated = [{{:ok, 1}, "Reason"}]
170+
assert deprecated == Sample.__info__(:deprecated)
171+
172+
assert %{{:ok, 1} => %{deprecated: "Reason", sig: _}} = exports(sample_binary())
173+
end
174+
175+
test "defines signatures without fallback to any" do
176+
exports = exports(sample_binary())
177+
178+
assert %{{:impl_for, 1} => %{sig: {:strong, domain, clauses}}} = exports
179+
assert domain == [term()]
180+
181+
assert clauses == [
182+
{[Of.impl(ImplStruct)], atom([Sample.Protocol.ConsolidationTest.ImplStruct])},
183+
{[negation(Of.impl(ImplStruct))], atom([nil])}
184+
]
168185

169-
test "consolidation extracts implementations with charlist path" do
170-
protos =
171-
Protocol.extract_impls(Enumerable, [to_charlist(Application.app_dir(:elixir, "ebin"))])
186+
assert %{{:impl_for!, 1} => %{sig: {:strong, domain, clauses}}} = exports
187+
assert domain == [Of.impl(ImplStruct)]
172188

173-
assert List in protos
174-
assert Function in protos
189+
assert clauses == [
190+
{[Of.impl(ImplStruct)], atom([Sample.Protocol.ConsolidationTest.ImplStruct])}
191+
]
192+
193+
assert %{{:ok, 1} => %{sig: {:strong, nil, clauses}}} = exports
194+
195+
assert clauses == [
196+
{[Of.impl(ImplStruct)], dynamic()}
197+
]
198+
end
199+
200+
test "defines signatures with fallback to any" do
201+
exports = exports(with_any_binary())
202+
203+
assert %{
204+
{:impl_for, 1} => %{sig: {:strong, domain, clauses}},
205+
{:impl_for!, 1} => %{sig: {:strong, domain, clauses}}
206+
} = exports
207+
208+
assert domain == [term()]
209+
210+
assert clauses == [
211+
{[Of.impl(Map)], atom([WithAny.Map])},
212+
{[Of.impl(ImplStruct)], atom([WithAny.Protocol.ConsolidationTest.ImplStruct])},
213+
{[negation(union(Of.impl(ImplStruct), Of.impl(Map)))], atom([WithAny.Any])}
214+
]
215+
216+
assert %{{:ok, 2} => %{sig: {:strong, nil, clauses}}} = exports
217+
218+
assert clauses == [
219+
{[term(), term()], dynamic()}
220+
]
221+
end
222+
223+
test "defines signatures without implementation" do
224+
exports = exports(no_impl_binary())
225+
226+
assert %{{:impl_for, 1} => %{sig: {:strong, domain, clauses}}} = exports
227+
assert domain == [term()]
228+
assert clauses == [{[term()], atom([nil])}]
229+
230+
assert %{{:impl_for!, 1} => %{sig: {:strong, domain, clauses}}} = exports
231+
assert domain == [none()]
232+
assert clauses == [{[none()], none()}]
233+
234+
assert %{{:ok, 1} => %{sig: {:strong, nil, clauses}}} = exports
235+
assert clauses == [{[none()], dynamic()}]
236+
end
175237
end
176238

177-
test "consolidation extracts implementations with binary path" do
178-
protos = Protocol.extract_impls(Enumerable, [Application.app_dir(:elixir, "ebin")])
179-
assert List in protos
180-
assert Function in protos
239+
test "consolidation errors on missing BEAM files" do
240+
defprotocol NoBeam do
241+
def example(arg)
242+
end
243+
244+
assert Protocol.consolidate(String, []) == {:error, :not_a_protocol}
245+
assert Protocol.consolidate(NoBeam, []) == {:error, :no_beam_info}
181246
end
182247

183248
test "protocol not implemented" do
@@ -191,4 +256,26 @@ defmodule Protocol.ConsolidationTest do
191256
sample.ok(:foo)
192257
end
193258
end
259+
260+
describe "extraction" do
261+
test "protocols" do
262+
protos = Protocol.extract_protocols([Application.app_dir(:elixir, "ebin")])
263+
assert Enumerable in protos
264+
assert Inspect in protos
265+
end
266+
267+
test "implementations with charlist path" do
268+
protos =
269+
Protocol.extract_impls(Enumerable, [to_charlist(Application.app_dir(:elixir, "ebin"))])
270+
271+
assert List in protos
272+
assert Function in protos
273+
end
274+
275+
test "implementations with binary path" do
276+
protos = Protocol.extract_impls(Enumerable, [Application.app_dir(:elixir, "ebin")])
277+
assert List in protos
278+
assert Function in protos
279+
end
280+
end
194281
end

0 commit comments

Comments
 (0)