From f101058bdeea754e8e1166d939741a149bbfd57a Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 18:10:07 -0400 Subject: [PATCH 1/8] Callback-related tests use beam AST to find callbacks --- lib/elixir/test/elixir/behaviour_test.exs | 25 +++++++------ lib/elixir/test/elixir/protocol_test.exs | 44 +++++++++++++---------- lib/elixir/test/elixir/test_helper.exs | 15 ++++++++ 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/lib/elixir/test/elixir/behaviour_test.exs b/lib/elixir/test/elixir/behaviour_test.exs index 9ad68abb98f..cd9b2a46df9 100644 --- a/lib/elixir/test/elixir/behaviour_test.exs +++ b/lib/elixir/test/elixir/behaviour_test.exs @@ -3,30 +3,33 @@ Code.require_file "test_helper.exs", __DIR__ defmodule BehaviourTest do use ExUnit.Case, async: true - defmodule Sample do - use Behaviour + {_, _, sample_binary, _} = + defmodule Sample do + use Behaviour - defcallback first(integer) :: integer + defcallback first(integer) :: integer - defcallback foo(atom(), binary) :: binary + defcallback foo(atom(), binary) :: binary - defcallback bar(External.hello, my_var :: binary) :: binary + defcallback bar(External.hello, my_var :: binary) :: binary - defcallback guarded(my_var) :: my_var when my_var: binary + defcallback guarded(my_var) :: my_var when my_var: binary - defcallback orr(atom | integer) :: atom + defcallback orr(atom | integer) :: atom - defcallback literal(123, {atom}, :atom, [integer], true) :: atom + defcallback literal(123, {atom}, :atom, [integer], true) :: atom - defmacrocallback last(integer) :: Macro.t - end + defmacrocallback last(integer) :: Macro.t + end + + @sample_binary sample_binary test "callbacks" do assert Sample.__behaviour__(:callbacks) == [first: 1, guarded: 1, "MACRO-last": 2, literal: 5, orr: 1, foo: 2, bar: 2] end test "specs" do - assert length(Keyword.get_values(Sample.module_info[:attributes], :callback)) == 7 + assert length(AbstractCodeHelpers.callbacks_for_beam(@sample_binary)) == 7 end test "default is not supported" do diff --git a/lib/elixir/test/elixir/protocol_test.exs b/lib/elixir/test/elixir/protocol_test.exs index 33b881999f2..3b57ef593f9 100644 --- a/lib/elixir/test/elixir/protocol_test.exs +++ b/lib/elixir/test/elixir/protocol_test.exs @@ -5,18 +5,24 @@ defmodule ProtocolTest do doctest Protocol - defprotocol Sample do - @type t :: any - @doc "Ok" - @spec ok(t) :: boolean - def ok(term) - end + {_, _, sample_binary, _} = + defprotocol Sample do + @type t :: any + @doc "Ok" + @spec ok(t) :: boolean + def ok(term) + end - defprotocol WithAny do - @fallback_to_any true - @doc "Ok" - def ok(term) - end + @sample_binary sample_binary + + {_, _, with_any_binary, _} = + defprotocol WithAny do + @fallback_to_any true + @doc "Ok" + def ok(term) + end + + @with_any_binary with_any_binary defprotocol Derivable do def ok(a) @@ -123,11 +129,11 @@ defmodule ProtocolTest do end test "protocol defines callbacks" do - assert get_callbacks(Sample, :ok, 1) == - [{:type, [11], :fun, [{:type, [11], :product, [{:user_type, [11], :t, []}]}, {:type, [11], :boolean, []}]}] + assert get_callbacks(@sample_binary, :ok, 1) == + [{:type, 12, :fun, [{:type, 12, :product, [{:user_type, 12, :t, []}]}, {:type, 12, :boolean, []}]}] - assert get_callbacks(WithAny, :ok, 1) == - [{:type, [18], :fun, [{:type, [18], :product, [{:user_type, [18], :t, []}]}, {:type, [18], :term, []}]}] + assert get_callbacks(@with_any_binary, :ok, 1) == + [{:type, 22, :fun, [{:type, 22, :product, [{:user_type, 22, :t, []}]}, {:type, 22, :term, []}]}] end test "protocol defines functions and attributes" do @@ -182,8 +188,8 @@ defmodule ProtocolTest do assert Multi.test(:a) == :a end - defp get_callbacks(module, name, arity) do - callbacks = for {:callback, info} <- module.__info__(:attributes), do: hd(info) + defp get_callbacks(beam, name, arity) do + callbacks = AbstractCodeHelpers.callbacks_for_beam(beam) List.keyfind(callbacks, {name, arity}, 0) |> elem(1) end @@ -308,6 +314,8 @@ defmodule Protocol.ConsolidationTest do {:ok, binary} = Protocol.consolidate(Sample, [Any, ImplStruct]) :code.load_binary(Sample, 'protocol_test.exs', binary) + @sample_binary binary + # Any should be moved to the end :code.purge(WithAny) :code.delete(WithAny) @@ -367,7 +375,7 @@ defmodule Protocol.ConsolidationTest do end test "consolidated keeps callbacks" do - callbacks = for {:callback, info} <- Sample.__info__(:attributes), do: hd(info) + callbacks = AbstractCodeHelpers.callbacks_for_beam(@sample_binary) assert callbacks != [] end diff --git a/lib/elixir/test/elixir/test_helper.exs b/lib/elixir/test/elixir/test_helper.exs index d919fb4ec04..b21867a646c 100644 --- a/lib/elixir/test/elixir/test_helper.exs +++ b/lib/elixir/test/elixir/test_helper.exs @@ -103,3 +103,18 @@ defmodule CompileAssertion do result || flunk("Expected expression to fail") end end + +defmodule AbstractCodeHelpers do + def callbacks_for_beam(beam) do + abstract_code = abstract_code_for_beam(beam) + + for {:attribute, _, :callback, value} <- abstract_code, do: value + end + + defp abstract_code_for_beam(beam) do + {:ok, {_, [abstract_code: {:raw_abstract_v1, abstract_code}]}} = + :beam_lib.chunks(beam, [:abstract_code]) + + abstract_code + end +end From 87d449abe922da84e259dde3045556b124e59719 Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 18:12:58 -0400 Subject: [PATCH 2/8] Fix invalid function call warning on OTP 19 --- lib/elixir/test/elixir/kernel/raise_test.exs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/elixir/test/elixir/kernel/raise_test.exs b/lib/elixir/test/elixir/kernel/raise_test.exs index 8915b1d9264..a2ed4054ba8 100644 --- a/lib/elixir/test/elixir/kernel/raise_test.exs +++ b/lib/elixir/test/elixir/kernel/raise_test.exs @@ -283,9 +283,10 @@ defmodule Kernel.RaiseTest do end test "badfun error" do - x = :example + # Avoid "invalid function call" warning in >= OTP 19 + x = fn -> :example end result = try do - x.(2) + x.().(2) rescue x in [BadFunctionError] -> Exception.message(x) end From c1ca7eb28b85c42fbc0347ed5460c817da1ed1ca Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 18:16:28 -0400 Subject: [PATCH 3/8] Skip dialyzer-related tests on OTP 19 --- lib/elixir/test/elixir/kernel/dialyzer_test.exs | 2 ++ lib/elixir/test/elixir/test_helper.exs | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/elixir/test/elixir/kernel/dialyzer_test.exs b/lib/elixir/test/elixir/kernel/dialyzer_test.exs index ba98f6d913d..cc0863ac56c 100644 --- a/lib/elixir/test/elixir/kernel/dialyzer_test.exs +++ b/lib/elixir/test/elixir/kernel/dialyzer_test.exs @@ -53,6 +53,7 @@ defmodule Kernel.DialyzerTest do {:ok, [outdir: dir, dialyzer: dialyzer]} end + @tag :does_not_apply_to_otp19 test "no warnings on valid remote calls", context do copy_beam! context, Dialyzer.RemoteCall assert_dialyze_no_warnings! context @@ -63,6 +64,7 @@ defmodule Kernel.DialyzerTest do assert_dialyze_no_warnings! context end + @tag :does_not_apply_to_otp19 test "no warnings on raise", context do copy_beam! context, Dialyzer.Raise assert_dialyze_no_warnings! context diff --git a/lib/elixir/test/elixir/test_helper.exs b/lib/elixir/test/elixir/test_helper.exs index b21867a646c..3bcc73e52ce 100644 --- a/lib/elixir/test/elixir/test_helper.exs +++ b/lib/elixir/test/elixir/test_helper.exs @@ -1,4 +1,10 @@ -ExUnit.start [trace: "--trace" in System.argv] +exclude = + case :erlang.system_info(:otp_release) do + '19' -> [:does_not_apply_to_otp19] + _ -> [] + end + +ExUnit.start [exclude: exclude, trace: "--trace" in System.argv] # Beam files compiled on demand path = Path.expand("../../tmp/beams", __DIR__) From 1bd0b165454dd143d26c34444bdb599faa942b2a Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 18:18:35 -0400 Subject: [PATCH 4/8] Skip callback-related IEx test on OTP 19 --- lib/iex/test/iex/helpers_test.exs | 1 + lib/iex/test/test_helper.exs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/iex/test/iex/helpers_test.exs b/lib/iex/test/iex/helpers_test.exs index b96b57cf0c7..6926709d63d 100644 --- a/lib/iex/test/iex/helpers_test.exs +++ b/lib/iex/test/iex/helpers_test.exs @@ -71,6 +71,7 @@ defmodule IEx.HelpersTest do cleanup_modules([Sample]) end + @tag :does_not_apply_to_otp19 test "h helper for callbacks" do behaviour = """ defmodule MyBehaviour do diff --git a/lib/iex/test/test_helper.exs b/lib/iex/test/test_helper.exs index c3d57182311..84ba2efab94 100644 --- a/lib/iex/test/test_helper.exs +++ b/lib/iex/test/test_helper.exs @@ -1,6 +1,13 @@ :ok = Application.start(:iex) IEx.configure([colors: [enabled: false]]) -ExUnit.start [trace: "--trace" in System.argv] + +exclude = + case :erlang.system_info(:otp_release) do + '19' -> [:does_not_apply_to_otp19] + _ -> [] + end + +ExUnit.start [exclude: exclude, trace: "--trace" in System.argv] defmodule IEx.Case do use ExUnit.CaseTemplate @@ -70,4 +77,3 @@ defmodule IEx.Case do |> String.trim end end - From 11723716f6c8b41e4ffc313314dc4943192cc5c8 Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 19:27:14 -0400 Subject: [PATCH 5/8] Fix IEx h helper for OTP 19 --- lib/iex/lib/iex/introspection.ex | 9 +++------ lib/iex/test/iex/helpers_test.exs | 1 - lib/iex/test/test_helper.exs | 9 +-------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/iex/lib/iex/introspection.ex b/lib/iex/lib/iex/introspection.ex index 09be4694b4a..a73f7cdd80f 100644 --- a/lib/iex/lib/iex/introspection.ex +++ b/lib/iex/lib/iex/introspection.ex @@ -108,7 +108,7 @@ defmodule IEx.Introspection do if docs = Code.get_docs(mod, :docs) do if doc = find_doc(docs, fun, arity) do if callback_module = is_nil(elem(doc, 4)) and callback_module(mod, fun, arity) do - filter = &match?({^fun, _}, elem(&1, 0)) + filter = &match?({^fun, ^arity}, elem(&1, 0)) print_callback_docs(callback_module, filter, &print_doc/2) else print_doc(doc) @@ -147,14 +147,11 @@ defmodule IEx.Introspection do do: true defp callback_module(mod, fun, arity) do + filter = &match?({{^fun, ^arity}, _}, &1) mod.module_info(:attributes) |> Keyword.get_values(:behaviour) |> Stream.concat() - |> Enum.find(fn module -> - module.module_info(:attributes) - |> Enum.filter(&match?({:callback, _}, &1)) - |> Enum.any?(&match?({_, [{{^fun, ^arity}, _} | _]}, &1)) - end) + |> Enum.find(&Enum.any?(Typespec.beam_callbacks(&1), filter)) end defp print_doc({{fun, _}, _line, kind, args, doc}) do diff --git a/lib/iex/test/iex/helpers_test.exs b/lib/iex/test/iex/helpers_test.exs index 6926709d63d..b96b57cf0c7 100644 --- a/lib/iex/test/iex/helpers_test.exs +++ b/lib/iex/test/iex/helpers_test.exs @@ -71,7 +71,6 @@ defmodule IEx.HelpersTest do cleanup_modules([Sample]) end - @tag :does_not_apply_to_otp19 test "h helper for callbacks" do behaviour = """ defmodule MyBehaviour do diff --git a/lib/iex/test/test_helper.exs b/lib/iex/test/test_helper.exs index 84ba2efab94..fc18f466282 100644 --- a/lib/iex/test/test_helper.exs +++ b/lib/iex/test/test_helper.exs @@ -1,13 +1,6 @@ :ok = Application.start(:iex) IEx.configure([colors: [enabled: false]]) - -exclude = - case :erlang.system_info(:otp_release) do - '19' -> [:does_not_apply_to_otp19] - _ -> [] - end - -ExUnit.start [exclude: exclude, trace: "--trace" in System.argv] +ExUnit.start [trace: "--trace" in System.argv] defmodule IEx.Case do use ExUnit.CaseTemplate From d0bb9a607b15983aa94a15191782c58c676c15e2 Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 19:32:20 -0400 Subject: [PATCH 6/8] Use Kernel.Typespec.beam_callbacks/1 instead of custom helper for tests --- lib/elixir/test/elixir/behaviour_test.exs | 2 +- lib/elixir/test/elixir/protocol_test.exs | 4 ++-- lib/elixir/test/elixir/test_helper.exs | 15 --------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/lib/elixir/test/elixir/behaviour_test.exs b/lib/elixir/test/elixir/behaviour_test.exs index cd9b2a46df9..f4093439163 100644 --- a/lib/elixir/test/elixir/behaviour_test.exs +++ b/lib/elixir/test/elixir/behaviour_test.exs @@ -29,7 +29,7 @@ defmodule BehaviourTest do end test "specs" do - assert length(AbstractCodeHelpers.callbacks_for_beam(@sample_binary)) == 7 + assert length(Kernel.Typespec.beam_callbacks(@sample_binary)) == 7 end test "default is not supported" do diff --git a/lib/elixir/test/elixir/protocol_test.exs b/lib/elixir/test/elixir/protocol_test.exs index 3b57ef593f9..e7d1752607d 100644 --- a/lib/elixir/test/elixir/protocol_test.exs +++ b/lib/elixir/test/elixir/protocol_test.exs @@ -189,7 +189,7 @@ defmodule ProtocolTest do end defp get_callbacks(beam, name, arity) do - callbacks = AbstractCodeHelpers.callbacks_for_beam(beam) + callbacks = Kernel.Typespec.beam_callbacks(beam) List.keyfind(callbacks, {name, arity}, 0) |> elem(1) end @@ -375,7 +375,7 @@ defmodule Protocol.ConsolidationTest do end test "consolidated keeps callbacks" do - callbacks = AbstractCodeHelpers.callbacks_for_beam(@sample_binary) + callbacks = Kernel.Typespec.beam_callbacks(@sample_binary) assert callbacks != [] end diff --git a/lib/elixir/test/elixir/test_helper.exs b/lib/elixir/test/elixir/test_helper.exs index 3bcc73e52ce..00b731f5531 100644 --- a/lib/elixir/test/elixir/test_helper.exs +++ b/lib/elixir/test/elixir/test_helper.exs @@ -109,18 +109,3 @@ defmodule CompileAssertion do result || flunk("Expected expression to fail") end end - -defmodule AbstractCodeHelpers do - def callbacks_for_beam(beam) do - abstract_code = abstract_code_for_beam(beam) - - for {:attribute, _, :callback, value} <- abstract_code, do: value - end - - defp abstract_code_for_beam(beam) do - {:ok, {_, [abstract_code: {:raw_abstract_v1, abstract_code}]}} = - :beam_lib.chunks(beam, [:abstract_code]) - - abstract_code - end -end From 5a055444630c2645f03a541a8083184939438f3a Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 19:36:27 -0400 Subject: [PATCH 7/8] Improve IEx h helper test for multi-arity callbacks --- lib/iex/test/iex/helpers_test.exs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/iex/test/iex/helpers_test.exs b/lib/iex/test/iex/helpers_test.exs index b96b57cf0c7..6d88851f6bc 100644 --- a/lib/iex/test/iex/helpers_test.exs +++ b/lib/iex/test/iex/helpers_test.exs @@ -77,14 +77,17 @@ defmodule IEx.HelpersTest do @doc "Docs for MyBehaviour.first" @callback first(integer) :: integer @callback second(integer) :: integer + @callback second(integer, integer) :: integer end """ impl = """ defmodule Impl do @behaviour MyBehaviour def first(0), do: 0 - @doc "Docs for Impl.second" + @doc "Docs for Impl.second/1" def second(0), do: 0 + @doc "Docs for Impl.second/2" + def second(0, 0), do: 0 end """ files = ["my_behaviour.ex", "impl.ex"] @@ -92,10 +95,11 @@ defmodule IEx.HelpersTest do assert c(files, ".") |> Enum.sort == [Impl, MyBehaviour] assert capture_io(fn -> h Impl.first/1 end) == "* @callback first(integer()) :: integer()\n\nDocs for MyBehaviour.first\n" - assert capture_io(fn -> h Impl.second/1 end) == "* def second(int)\n\nDocs for Impl.second\n" + assert capture_io(fn -> h Impl.second/1 end) == "* def second(int)\n\nDocs for Impl.second/1\n" + assert capture_io(fn -> h Impl.second/2 end) == "* def second(int1, int2)\n\nDocs for Impl.second/2\n" assert capture_io(fn -> h Impl.first end) == "* @callback first(integer()) :: integer()\n\nDocs for MyBehaviour.first\n" - assert capture_io(fn -> h Impl.second end) == "* def second(int)\n\nDocs for Impl.second\n" + assert capture_io(fn -> h Impl.second end) == "* def second(int)\n\nDocs for Impl.second/1\n* def second(int1, int2)\n\nDocs for Impl.second/2\n" end after cleanup_modules([Impl, MyBehaviour]) From dcc4fd180d21680da85312b9935d44125b9206d6 Mon Sep 17 00:00:00 2001 From: Eric Entin Date: Wed, 22 Jun 2016 19:38:57 -0400 Subject: [PATCH 8/8] Change otp19 exclusion tag name --- lib/elixir/test/elixir/kernel/dialyzer_test.exs | 4 ++-- lib/elixir/test/elixir/test_helper.exs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/elixir/test/elixir/kernel/dialyzer_test.exs b/lib/elixir/test/elixir/kernel/dialyzer_test.exs index cc0863ac56c..6bfd4788580 100644 --- a/lib/elixir/test/elixir/kernel/dialyzer_test.exs +++ b/lib/elixir/test/elixir/kernel/dialyzer_test.exs @@ -53,7 +53,7 @@ defmodule Kernel.DialyzerTest do {:ok, [outdir: dir, dialyzer: dialyzer]} end - @tag :does_not_apply_to_otp19 + @tag otp19: false test "no warnings on valid remote calls", context do copy_beam! context, Dialyzer.RemoteCall assert_dialyze_no_warnings! context @@ -64,7 +64,7 @@ defmodule Kernel.DialyzerTest do assert_dialyze_no_warnings! context end - @tag :does_not_apply_to_otp19 + @tag otp19: false test "no warnings on raise", context do copy_beam! context, Dialyzer.Raise assert_dialyze_no_warnings! context diff --git a/lib/elixir/test/elixir/test_helper.exs b/lib/elixir/test/elixir/test_helper.exs index 00b731f5531..c84f4f9bfc6 100644 --- a/lib/elixir/test/elixir/test_helper.exs +++ b/lib/elixir/test/elixir/test_helper.exs @@ -1,6 +1,6 @@ exclude = case :erlang.system_info(:otp_release) do - '19' -> [:does_not_apply_to_otp19] + '19' -> [otp19: false] _ -> [] end