Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d98af49c9 | ||
|
|
98485daab0 | ||
|
|
59f47949a9 | ||
|
|
fca5f1cf73 | ||
|
|
153ec434de | ||
|
|
061a989935 | ||
|
|
21f10f7296 | ||
|
|
336452f81b | ||
|
|
511a51ba89 | ||
|
|
7e4f9c5647 | ||
|
|
b37ecb0f8e | ||
|
|
ac947a5d1d | ||
|
|
82a511aafe | ||
|
|
7c2e65f418 | ||
|
|
3d04cb0aa0 | ||
|
|
8fa9d728f1 |
+36
-1
@@ -63,7 +63,42 @@ When a task is spawned directly from your code, without a supervisor, then the p
|
||||
|
||||
This small feature is very powerful. It allows instrumentation and monitoring tools to better track and relate the events happening in your system. This feature can also be used by tools like the "Ecto Sandbox". The "Ecto Sandbox" allows developers to run tests concurrently against the database, by using transactions and an ownership mechanism where each process explicitly gets a connection assigned to it. Without `$callers`, every time you spawned a task that queries the database, the task would not know its caller, and therefore it would be unable to know which connection was assigned to it. This often meant features that relies on tasks could not be tested concurrently. With `$callers`, figuring out this relationship is trivial and you have more tests using the full power of your machine.
|
||||
|
||||
## v1.8.0 (2018-01-14)
|
||||
## v1.8.2 (2019-05-11)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### EEx
|
||||
|
||||
* [EEx] Raise readable error message on bad EEx state
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Protocol] Ensure `:debug_info` is kept in protocols
|
||||
|
||||
#### Logger
|
||||
|
||||
* [Logger] Make sure Logger v1.8 does not get stuck in discard mode
|
||||
* [Logger.Translator] Translate remote process crash in Logger
|
||||
|
||||
## v1.8.1 (2019-01-30)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Float] Fix rounding for subnormal floats
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx] Fix `IEx.pry` crash when IEx isn't running
|
||||
* [IEx.CLI] Add IEx warning when using `--remsh` with dumb terminal
|
||||
* [IEx.Helpers] Sort results by arity on `h` helper
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix compile] Do not include optional dependencies in extra applications as it is incompatible with shared deps in umbrellas
|
||||
|
||||
## v1.8.0 (2019-01-14)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ defmodule EEx.Engine do
|
||||
|
||||
@doc false
|
||||
def handle_begin(state) do
|
||||
check_state!(state)
|
||||
%{state | binary: [], dynamic: []}
|
||||
end
|
||||
|
||||
@@ -168,6 +169,7 @@ defmodule EEx.Engine do
|
||||
|
||||
@doc false
|
||||
def handle_body(state) do
|
||||
check_state!(state)
|
||||
%{binary: binary, dynamic: dynamic} = state
|
||||
binary = {:<<>>, [], Enum.reverse(binary)}
|
||||
dynamic = [binary | dynamic]
|
||||
@@ -207,4 +209,11 @@ defmodule EEx.Engine do
|
||||
raise EEx.SyntaxError,
|
||||
"unsupported EEx syntax <%#{marker} %> (the syntax is valid but not supported by the current EEx engine)"
|
||||
end
|
||||
|
||||
defp check_state!(%{binary: _, dynamic: _, vars_count: _}), do: :ok
|
||||
|
||||
defp check_state!(state) do
|
||||
raise "unexpected EEx.Engine state: #{inspect(state)}. " <>
|
||||
"This typically means a bug or an outdated EEx.Engine or tool"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -480,9 +480,11 @@ defmodule DateTime do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> {:ok, datetime} = DateTime.now("Europe/Copenhagen", FakeTimeZoneDatabase)
|
||||
iex> {:ok, datetime} = DateTime.now("Etc/UTC")
|
||||
iex> datetime.time_zone
|
||||
"Europe/Copenhagen"
|
||||
"Etc/UTC"
|
||||
iex> DateTime.now("Europe/Copenhagen")
|
||||
{:error, :utc_only_time_zone_database}
|
||||
iex> DateTime.now("not a real time zone name", FakeTimeZoneDatabase)
|
||||
{:error, :time_zone_not_found}
|
||||
|
||||
|
||||
@@ -268,16 +268,14 @@ defmodule Float do
|
||||
raise ArgumentError, invalid_precision_message(precision)
|
||||
end
|
||||
|
||||
defp round(0.0, _precision, _rounding), do: 0.0
|
||||
|
||||
defp round(float, precision, rounding) do
|
||||
<<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
|
||||
{num, count, _} = decompose(significant, 1)
|
||||
count = count - exp + 1023
|
||||
|
||||
cond do
|
||||
# There is no decimal precision on subnormal floats
|
||||
count <= 0 or exp == 0 ->
|
||||
float
|
||||
|
||||
# Precision beyond 15 digits
|
||||
count >= 104 ->
|
||||
case rounding do
|
||||
@@ -444,11 +442,11 @@ defmodule Float do
|
||||
{acc, last_count, last_power}
|
||||
end
|
||||
|
||||
@compile {:inline, sign: 2, shift_left: 2}
|
||||
defp sign(0, num), do: num
|
||||
defp sign(1, num), do: -num
|
||||
|
||||
defp shift_left(num, 0), do: num
|
||||
defp shift_left(num, times), do: shift_left(num <<< 1, times - 1)
|
||||
defp shift_left(num, times), do: num <<< times
|
||||
|
||||
defp shift_right(num, 0), do: {num, 0}
|
||||
defp shift_right(1, times), do: {1, times}
|
||||
|
||||
@@ -135,7 +135,7 @@ defmodule GenServer do
|
||||
|
||||
Supervisor.start_link(children, strategy: :one_for_all)
|
||||
|
||||
In both cases, `Stack.start_link/1` is alwaus invoked.
|
||||
In both cases, `Stack.start_link/1` is always invoked.
|
||||
|
||||
`use GenServer` also accepts a list of options which configures the
|
||||
child specification and therefore how it runs under a supervisor.
|
||||
|
||||
@@ -49,8 +49,8 @@ Elixir version | Supported Erlang/OTP versions
|
||||
1.4 | 18 - 19 (and Erlang/OTP 20 from v1.4.5)
|
||||
1.5 | 18 - 20
|
||||
1.6 | 19 - 20 (and Erlang/OTP 21 from v1.6.6)
|
||||
1.7 | 19 - 21
|
||||
1.8 | 20 - 21
|
||||
1.7 | 19 - 22
|
||||
1.8 | 20 - 22
|
||||
|
||||
While Elixir often adds compatibility to new Erlang/OTP versions on released branches, such as support for Erlang/OTP 20 in v1.4.5, those releases usually contain the minimum changes for Elixir to run without errors. Only the next minor release, in this case v1.5.0, does effectively leverage the new features provided by the latest Erlang/OTP release.
|
||||
|
||||
|
||||
@@ -446,10 +446,10 @@ debug_opts(Map, Specs, Opts) ->
|
||||
end.
|
||||
|
||||
take_debug_opts(Opts) ->
|
||||
case lists:keytake(debug_info, 1, Opts) of
|
||||
{value, {debug_info, true}, Rest} -> {true, Rest};
|
||||
{value, {debug_info, false}, Rest} -> {false, Rest};
|
||||
false -> {elixir_compiler:get_opt(debug_info), Opts}
|
||||
case proplists:get_value(debug_info, Opts) of
|
||||
true -> {true, proplists:delete(debug_info, Opts)};
|
||||
false -> {false, proplists:delete(debug_info, Opts)};
|
||||
undefined -> {elixir_compiler:get_opt(debug_info), Opts}
|
||||
end.
|
||||
|
||||
extra_chunks_opts([], Opts) -> Opts;
|
||||
|
||||
@@ -55,21 +55,41 @@ defmodule FloatTest do
|
||||
assert Float.floor(1.32453e-10) === 0.0
|
||||
end
|
||||
|
||||
test "floor/2 with precision" do
|
||||
assert Float.floor(12.524235, 0) === 12.0
|
||||
assert Float.floor(-12.524235, 0) === -13.0
|
||||
describe "floor/2" do
|
||||
test "with 0.0" do
|
||||
for precision <- 0..15 do
|
||||
assert Float.floor(0.0, precision) === 0.0
|
||||
assert Float.floor(-0.0, precision) === -0.0
|
||||
end
|
||||
end
|
||||
|
||||
assert Float.floor(12.52, 2) === 12.51
|
||||
assert Float.floor(-12.52, 2) === -12.52
|
||||
test "floor/2 with precision" do
|
||||
assert Float.floor(12.524235, 0) === 12.0
|
||||
assert Float.floor(-12.524235, 0) === -13.0
|
||||
|
||||
assert Float.floor(12.524235, 2) === 12.52
|
||||
assert Float.floor(-12.524235, 3) === -12.525
|
||||
assert Float.floor(12.52, 2) === 12.51
|
||||
assert Float.floor(-12.52, 2) === -12.52
|
||||
|
||||
assert Float.floor(12.32453e-20, 2) === 0.0
|
||||
assert Float.floor(-12.32453e-20, 2) === -0.01
|
||||
assert Float.floor(12.524235, 2) === 12.52
|
||||
assert Float.floor(-12.524235, 3) === -12.525
|
||||
|
||||
assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
|
||||
Float.floor(1.1, 16)
|
||||
assert Float.floor(12.32453e-20, 2) === 0.0
|
||||
assert Float.floor(-12.32453e-20, 2) === -0.01
|
||||
|
||||
assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
|
||||
Float.floor(1.1, 16)
|
||||
end
|
||||
end
|
||||
|
||||
test "with subnormal floats" do
|
||||
assert Float.floor(-5.0e-324, 0) === -1.0
|
||||
assert Float.floor(-5.0e-324, 1) === -0.1
|
||||
assert Float.floor(-5.0e-324, 2) === -0.01
|
||||
assert Float.floor(-5.0e-324, 15) === -0.000000000000001
|
||||
|
||||
for precision <- 0..15 do
|
||||
assert Float.floor(5.0e-324, precision) === 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -88,36 +108,72 @@ defmodule FloatTest do
|
||||
assert Float.ceil(0.0) === 0.0
|
||||
end
|
||||
|
||||
test "ceil/2 with precision" do
|
||||
assert Float.ceil(12.524235, 0) === 13.0
|
||||
assert Float.ceil(-12.524235, 0) === -12.0
|
||||
describe "ceil/2" do
|
||||
test "with 0.0" do
|
||||
for precision <- 0..15 do
|
||||
assert Float.ceil(0.0, precision) === 0.0
|
||||
assert Float.ceil(-0.0, precision) === -0.0
|
||||
end
|
||||
end
|
||||
|
||||
assert Float.ceil(12.52, 2) === 12.52
|
||||
assert Float.ceil(-12.52, 2) === -12.51
|
||||
test "with regular floats" do
|
||||
assert Float.ceil(12.524235, 0) === 13.0
|
||||
assert Float.ceil(-12.524235, 0) === -12.0
|
||||
|
||||
assert Float.ceil(12.524235, 2) === 12.53
|
||||
assert Float.ceil(-12.524235, 3) === -12.524
|
||||
assert Float.ceil(12.52, 2) === 12.52
|
||||
assert Float.ceil(-12.52, 2) === -12.51
|
||||
|
||||
assert Float.ceil(12.32453e-20, 2) === 0.01
|
||||
assert Float.ceil(-12.32453e-20, 2) === 0.0
|
||||
assert Float.ceil(12.524235, 2) === 12.53
|
||||
assert Float.ceil(-12.524235, 3) === -12.524
|
||||
|
||||
assert Float.ceil(0.0, 2) === 0.0
|
||||
assert Float.ceil(12.32453e-20, 2) === 0.01
|
||||
assert Float.ceil(-12.32453e-20, 2) === 0.0
|
||||
|
||||
assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
|
||||
Float.ceil(1.1, 16)
|
||||
assert Float.ceil(0.0, 2) === 0.0
|
||||
|
||||
assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
|
||||
Float.ceil(1.1, 16)
|
||||
end
|
||||
end
|
||||
|
||||
test "with subnormal floats" do
|
||||
assert Float.ceil(5.0e-324, 0) === 1.0
|
||||
assert Float.ceil(5.0e-324, 1) === 0.1
|
||||
assert Float.ceil(5.0e-324, 2) === 0.01
|
||||
assert Float.ceil(5.0e-324, 15) === 0.000000000000001
|
||||
|
||||
for precision <- 0..15 do
|
||||
assert Float.ceil(-5.0e-324, precision) === -0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "round/2" do
|
||||
assert Float.round(5.5675, 3) === 5.567
|
||||
assert Float.round(-5.5674, 3) === -5.567
|
||||
assert Float.round(5.5, 3) === 5.5
|
||||
assert Float.round(5.5e-10, 10) === 5.0e-10
|
||||
assert Float.round(5.5e-10, 8) === 0.0
|
||||
assert Float.round(5.0, 0) === 5.0
|
||||
describe "round/2" do
|
||||
test "with 0.0" do
|
||||
for precision <- 0..15 do
|
||||
assert Float.round(0.0, precision) === 0.0
|
||||
assert Float.round(-0.0, precision) === -0.0
|
||||
end
|
||||
end
|
||||
|
||||
assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
|
||||
Float.round(1.1, 16)
|
||||
test "with regular floats" do
|
||||
assert Float.round(5.5675, 3) === 5.567
|
||||
assert Float.round(-5.5674, 3) === -5.567
|
||||
assert Float.round(5.5, 3) === 5.5
|
||||
assert Float.round(5.5e-10, 10) === 5.0e-10
|
||||
assert Float.round(5.5e-10, 8) === 0.0
|
||||
assert Float.round(5.0, 0) === 5.0
|
||||
|
||||
assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
|
||||
Float.round(1.1, 16)
|
||||
end
|
||||
end
|
||||
|
||||
test "with subnormal floats" do
|
||||
for precision <- 0..15 do
|
||||
assert Float.round(5.0e-324, precision) === 0.0
|
||||
assert Float.round(-5.0e-324, precision) === -0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -126,6 +182,12 @@ defmodule FloatTest do
|
||||
assert Float.ratio(0.0) == {0, 1}
|
||||
end
|
||||
|
||||
test "with regular floats" do
|
||||
assert Float.ratio(3.14) == {7_070_651_414_971_679, 2_251_799_813_685_248}
|
||||
assert Float.ratio(-3.14) == {-7_070_651_414_971_679, 2_251_799_813_685_248}
|
||||
assert Float.ratio(1.5) == {3, 2}
|
||||
end
|
||||
|
||||
test "with subnormal floats" do
|
||||
assert Float.ratio(5.0e-324) ==
|
||||
{1,
|
||||
|
||||
@@ -260,6 +260,25 @@ defmodule ProtocolTest do
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Protocol.DebugInfoTest do
|
||||
use ExUnit.Case
|
||||
|
||||
test "protocols always keep debug_info" do
|
||||
Code.compiler_options(debug_info: false)
|
||||
|
||||
{:module, _, binary, _} =
|
||||
defprotocol DebugInfoProto do
|
||||
end
|
||||
|
||||
assert {:ok, {DebugInfoProto, [debug_info: debug_info]}} =
|
||||
:beam_lib.chunks(binary, [:debug_info])
|
||||
|
||||
assert {:debug_info_v1, :elixir_erl, {:elixir_v1, _, _}} = debug_info
|
||||
after
|
||||
Code.compiler_options(debug_info: true)
|
||||
end
|
||||
end
|
||||
|
||||
path = Path.expand("../ebin", __DIR__)
|
||||
File.mkdir_p!(path)
|
||||
|
||||
|
||||
@@ -82,7 +82,13 @@ defmodule IEx.Broker do
|
||||
@spec take_over(binary, keyword) ::
|
||||
{:ok, server :: pid, group_leader :: pid} | {:error, :no_iex | :refused}
|
||||
def take_over(identifier, opts) do
|
||||
GenServer.call(@name, {:take_over, identifier, opts}, :infinity)
|
||||
case GenServer.whereis(@name) do
|
||||
nil ->
|
||||
{:error, :no_iex}
|
||||
|
||||
_pid ->
|
||||
GenServer.call(@name, {:take_over, identifier, opts}, :infinity)
|
||||
end
|
||||
end
|
||||
|
||||
## Callbacks
|
||||
|
||||
@@ -53,6 +53,13 @@ defmodule IEx.CLI do
|
||||
if tty_works?() do
|
||||
:user_drv.start([:"tty_sl -c -e", tty_args()])
|
||||
else
|
||||
if get_remsh(:init.get_plain_arguments()) do
|
||||
IO.puts(
|
||||
:stderr,
|
||||
"warning: the --remsh option will be ignored because IEx is running on limited shell"
|
||||
)
|
||||
end
|
||||
|
||||
:application.set_env(:stdlib, :shell_prompt_func, {__MODULE__, :prompt})
|
||||
:user.start()
|
||||
local_start()
|
||||
|
||||
@@ -283,6 +283,7 @@ defmodule IEx.Introspection do
|
||||
true ->
|
||||
module.module_info(:exports)
|
||||
end
|
||||
|> Enum.sort()
|
||||
|
||||
result =
|
||||
for {^function, arity} <- exports,
|
||||
|
||||
@@ -343,6 +343,12 @@ defmodule IEx.HelpersTest do
|
||||
"""
|
||||
|
||||
assert capture_io(fn -> h(:timer.send_interval()) end) == """
|
||||
* :timer.send_interval/2
|
||||
|
||||
@spec send_interval(time, message) :: {:ok, tRef} | {:error, reason}
|
||||
when time: time(), message: term(), tRef: tref(), reason: term()
|
||||
|
||||
Module was compiled without docs. Showing only specs.
|
||||
* :timer.send_interval/3
|
||||
|
||||
@spec send_interval(time, pid, message) :: {:ok, tRef} | {:error, reason}
|
||||
@@ -352,12 +358,6 @@ defmodule IEx.HelpersTest do
|
||||
tRef: tref(),
|
||||
reason: term()
|
||||
|
||||
Module was compiled without docs. Showing only specs.
|
||||
* :timer.send_interval/2
|
||||
|
||||
@spec send_interval(time, message) :: {:ok, tRef} | {:error, reason}
|
||||
when time: time(), message: term(), tRef: tref(), reason: term()
|
||||
|
||||
Module was compiled without docs. Showing only specs.
|
||||
"""
|
||||
end
|
||||
|
||||
@@ -555,6 +555,7 @@ defmodule Logger do
|
||||
:level,
|
||||
:utc_log,
|
||||
:discard_threshold,
|
||||
:discard_threshold_periodic_check,
|
||||
:translator_inspect_opts
|
||||
]
|
||||
@spec configure(keyword) :: :ok
|
||||
|
||||
@@ -4,6 +4,7 @@ defmodule Logger.Config do
|
||||
@behaviour :gen_event
|
||||
@name __MODULE__
|
||||
@table __MODULE__
|
||||
@check_discard {__MODULE__, :check_discard}
|
||||
|
||||
def start_link do
|
||||
GenServer.start_link(__MODULE__, :ok, name: @name)
|
||||
@@ -76,29 +77,7 @@ defmodule Logger.Config do
|
||||
end
|
||||
|
||||
def handle_event(_event, {state, thresholds}) do
|
||||
%{mode: mode} = state
|
||||
|
||||
case compute_mode(mode, thresholds) do
|
||||
^mode ->
|
||||
{:ok, {state, thresholds}}
|
||||
|
||||
new_mode ->
|
||||
if new_mode == :discard do
|
||||
message =
|
||||
"Logger has #{message_queue_length()} messages in its queue, " <>
|
||||
"which is above :discard_threshold. Messages will be discarded " <>
|
||||
"until the message queue goes back to 75% of the threshold size"
|
||||
|
||||
log(:warn, message, state)
|
||||
end
|
||||
|
||||
if mode == :discard do
|
||||
log(:warn, "Logger has stopped discarding messages", state)
|
||||
end
|
||||
|
||||
state = persist(%{state | mode: new_mode})
|
||||
{:ok, {state, thresholds}}
|
||||
end
|
||||
{:ok, {compute_mode_and_persist_state(state, thresholds), thresholds}}
|
||||
end
|
||||
|
||||
def handle_call({:configure, options}, {%{mode: mode}, _}) do
|
||||
@@ -127,6 +106,16 @@ defmodule Logger.Config do
|
||||
{:ok, old, state}
|
||||
end
|
||||
|
||||
def handle_info(@check_discard, {%{mode: :discard} = state, thresholds}) do
|
||||
state = compute_mode_and_persist_state(state, thresholds)
|
||||
|
||||
if state.mode == :discard do
|
||||
Process.send_after(self(), @check_discard, state.discard_threshold_periodic_check)
|
||||
end
|
||||
|
||||
{:ok, {state, thresholds}}
|
||||
end
|
||||
|
||||
def handle_info(_msg, state) do
|
||||
{:ok, state}
|
||||
end
|
||||
@@ -139,6 +128,31 @@ defmodule Logger.Config do
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
defp compute_mode_and_persist_state(%{mode: mode} = state, thresholds) do
|
||||
case compute_mode(mode, thresholds) do
|
||||
^mode ->
|
||||
state
|
||||
|
||||
new_mode ->
|
||||
if new_mode == :discard do
|
||||
Process.send_after(self(), @check_discard, state.discard_threshold_periodic_check)
|
||||
|
||||
message =
|
||||
"Logger has #{message_queue_length()} messages in its queue, " <>
|
||||
"which is above :discard_threshold. Messages will be discarded " <>
|
||||
"until the message queue goes back to 75% of the threshold size"
|
||||
|
||||
log(:warn, message, state)
|
||||
end
|
||||
|
||||
if mode == :discard do
|
||||
log(:warn, "Logger has stopped discarding messages", state)
|
||||
end
|
||||
|
||||
persist(%{state | mode: new_mode})
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
defp log(level, message, state) do
|
||||
@@ -163,7 +177,9 @@ defmodule Logger.Config do
|
||||
level: Application.get_env(:logger, :level),
|
||||
translators: Application.get_env(:logger, :translators),
|
||||
truncate: Application.get_env(:logger, :truncate),
|
||||
utc_log: Application.get_env(:logger, :utc_log)
|
||||
utc_log: Application.get_env(:logger, :utc_log),
|
||||
discard_threshold_periodic_check:
|
||||
Application.get_env(:logger, :discard_threshold_periodic_check)
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
@@ -126,6 +126,19 @@ defmodule Logger.Translator do
|
||||
|
||||
{:ok, msg, metadata}
|
||||
|
||||
{'Error in process ' ++ _, [pid, node, {reason, stack}]} ->
|
||||
reason = Exception.normalize(:error, reason, stack)
|
||||
|
||||
msg = [
|
||||
"Process ",
|
||||
inspect(pid),
|
||||
" on node ",
|
||||
inspect(node),
|
||||
" raised an exception" | format(:error, reason, stack)
|
||||
]
|
||||
|
||||
{:ok, msg, [crash_reason: exit_reason(:error, reason, stack)]}
|
||||
|
||||
{'Error in process ' ++ _, [pid, {reason, stack}]} ->
|
||||
reason = Exception.normalize(:error, reason, stack)
|
||||
msg = ["Process ", inspect(pid), " raised an exception" | format(:error, reason, stack)]
|
||||
|
||||
@@ -24,6 +24,7 @@ defmodule Logger.MixProject do
|
||||
handle_otp_reports: true,
|
||||
handle_sasl_reports: false,
|
||||
discard_threshold_for_error_logger: 500,
|
||||
discard_threshold_periodic_check: 30_000,
|
||||
compile_time_purge_level: :debug,
|
||||
compile_time_purge_matching: [],
|
||||
compile_time_application: nil,
|
||||
|
||||
@@ -12,11 +12,29 @@ defmodule Logger.ConfigTest do
|
||||
end
|
||||
|
||||
test "log/2 relies on discard_threshold" do
|
||||
Logger.remove_backend(:console)
|
||||
Logger.configure(discard_threshold: 0)
|
||||
for _ <- 1..1000, do: Logger.log(:info, "some message")
|
||||
after
|
||||
Logger.configure(discard_threshold: 10000)
|
||||
end
|
||||
|
||||
test "log/2 recovers from discard_threshold" do
|
||||
Logger.remove_backend(:console)
|
||||
Logger.configure(discard_threshold: 1, discard_threshold_periodic_check: 0)
|
||||
|
||||
:sys.suspend(Logger)
|
||||
assert Logger.log(:info, "BEGIN") == :ok
|
||||
send(Logger, :garbage)
|
||||
:sys.resume(Logger)
|
||||
|
||||
# Simulate a sync operation to make sure we are in :discard mode
|
||||
Logger.add_translator({Unknown, :translate})
|
||||
Logger.remove_translator({Unknown, :translate})
|
||||
|
||||
# It should eventually heal
|
||||
wait_for_mode(:async)
|
||||
after
|
||||
Logger.configure(discard_threshold: 10000, discard_threshold_periodic_check: 30000)
|
||||
Logger.add_backend(:console)
|
||||
end
|
||||
|
||||
@@ -37,4 +55,11 @@ defmodule Logger.ConfigTest do
|
||||
Logger.configure(level: :debug)
|
||||
end
|
||||
end
|
||||
|
||||
def wait_for_mode(mode) do
|
||||
unless Logger.Config.__data__().mode == mode do
|
||||
Process.sleep(10)
|
||||
wait_for_mode(mode)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -981,6 +981,19 @@ defmodule Logger.TranslatorTest do
|
||||
assert {:stop, [_ | _]} = process_metadata[:crash_reason]
|
||||
end
|
||||
|
||||
test "translates process crash with erts" do
|
||||
assert {:ok, msg, meta} =
|
||||
Logger.Translator.translate(
|
||||
:error,
|
||||
:error,
|
||||
:format,
|
||||
{'Error in process ~p on node ~p with exit value:~n~p~n',
|
||||
[self(), :"name@127.0.0.1", {:badarith, [{:erlang, :/, [1, 0], []}]}]}
|
||||
)
|
||||
|
||||
assert Keyword.get(meta, :crash_reason)
|
||||
end
|
||||
|
||||
test "reports :undefined MFA properly" do
|
||||
defmodule WeirdFunctionNamesGenServer do
|
||||
use GenServer
|
||||
|
||||
@@ -132,13 +132,18 @@ defmodule Mix.Dep do
|
||||
for dep <- deps,
|
||||
dep.app == app,
|
||||
child <- dep.deps,
|
||||
do: {child.app, true},
|
||||
do: {child.app, Keyword.get(child.opts, :optional, false)},
|
||||
into: %{}
|
||||
|
||||
Enum.map(children, fn %{app: app} = dep ->
|
||||
Enum.map(children, fn %{app: app, opts: opts} = dep ->
|
||||
# optional only matters at the top level. Any non-top level dependency
|
||||
# that is optional and is still available means it has been fulfilled.
|
||||
case top_level do
|
||||
%{^app => _} -> %{dep | top_level: true}
|
||||
%{} -> %{dep | top_level: false}
|
||||
%{^app => optional} ->
|
||||
%{dep | top_level: true, opts: Keyword.put(opts, :optional, optional)}
|
||||
|
||||
%{} ->
|
||||
%{dep | top_level: false, opts: Keyword.delete(opts, :optional)}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -212,7 +212,7 @@ defmodule Mix.Tasks.Compile.App do
|
||||
apps =
|
||||
properties
|
||||
|> Keyword.get(:applications)
|
||||
|> Kernel.||(apps_from_prod_deps(properties, config))
|
||||
|> Kernel.||(apps_from_prod_non_optional_deps(properties, config))
|
||||
|> normalize_apps(extra, config)
|
||||
|
||||
Keyword.put(properties, :applications, apps)
|
||||
@@ -317,11 +317,12 @@ defmodule Mix.Tasks.Compile.App do
|
||||
end)
|
||||
end
|
||||
|
||||
defp apps_from_prod_deps(properties, config) do
|
||||
defp apps_from_prod_non_optional_deps(properties, config) do
|
||||
included_applications = Keyword.get(properties, :included_applications, [])
|
||||
non_runtime_deps = non_runtime_deps(config)
|
||||
|
||||
for %{app: app, top_level: true} <- Mix.Dep.cached(),
|
||||
for %{app: app, opts: opts, top_level: true} <- Mix.Dep.cached(),
|
||||
not Keyword.get(opts, :optional, false),
|
||||
not Map.has_key?(non_runtime_deps, app),
|
||||
app not in included_applications,
|
||||
do: app
|
||||
|
||||
@@ -196,6 +196,31 @@ defmodule Mix.DepTest do
|
||||
end)
|
||||
end
|
||||
|
||||
test "nested deps with optional matching" do
|
||||
Process.put(:custom_deps_git_repo_opts, optional: true)
|
||||
|
||||
# deps_repo brings git_repo but it is optional
|
||||
deps = [
|
||||
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
|
||||
{:git_repo, "0.1.0", git: MixTest.Case.fixture_path("git_repo")}
|
||||
]
|
||||
|
||||
with_deps(deps, fn ->
|
||||
in_fixture("deps_status", fn ->
|
||||
File.mkdir_p!("custom/deps_repo/lib")
|
||||
|
||||
File.write!("custom/deps_repo/lib/a.ex", """
|
||||
# Check that the child dependency is top_level and optional
|
||||
[%Mix.Dep{app: :git_repo, top_level: true, opts: opts}] = Mix.Dep.cached()
|
||||
true = Keyword.fetch!(opts, :optional)
|
||||
""")
|
||||
|
||||
Mix.Tasks.Deps.Get.run([])
|
||||
Mix.Tasks.Deps.Compile.run([])
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
test "nested deps with convergence and optional dependencies" do
|
||||
deps = [
|
||||
{:deps_repo, "0.1.0", path: "custom/deps_repo"},
|
||||
|
||||
@@ -99,7 +99,7 @@ defmodule Mix.Tasks.Compile.AppTest do
|
||||
properties = parse_resource_file(:custom_deps)
|
||||
|
||||
assert properties[:applications] ==
|
||||
[:kernel, :stdlib, :elixir, :logger, :ok1, :ok3, :ok4, :ok6, :ok7]
|
||||
[:kernel, :stdlib, :elixir, :logger, :ok1, :ok3, :ok4, :ok7]
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user